simple_xlsx_reader 1.0.2 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +7 -0
- data/CHANGELOG.md +11 -0
- data/README.md +1 -1
- data/lib/simple_xlsx_reader.rb +93 -23
- data/lib/simple_xlsx_reader/version.rb +1 -1
- data/simple_xlsx_reader.gemspec +4 -2
- data/test/gdocs_sheet.xlsx +0 -0
- data/test/gdocs_sheet_test.rb +15 -0
- data/test/performance_test.rb +2 -2
- data/test/sesame_street_blog.xlsx +0 -0
- data/test/simple_xlsx_reader_test.rb +78 -11
- metadata +25 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1dd4b96753ca2dafcfadf59469c3a9357742ab7e
|
4
|
+
data.tar.gz: ffb23c6b020a55b893743206c236fea2abb07b65
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 01a2326f942c455c665d5c135aa4115edd87a307e4aa5dff5e1d177ef61dcd326bcc33ad342f3b164ac7996731f0e31e97a9471973b98682757624f44052f6a2
|
7
|
+
data.tar.gz: b5371713dd232d5f1134ab810ea69447a3ad59bb781f917df2bc3a49e7b1c86c765ab0a074b1ddce021390f1b1d54e1507326abf22a65f1d83de1cf568ce7896
|
data/.travis.yml
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
### 1.0.4
|
2
|
+
|
3
|
+
* Fix Windows + RubyZip 1.2.1 bug preventing files from being read
|
4
|
+
* Add ability to parse hyperlinks
|
5
|
+
* Support files exported from Google Docs (@Strnadj)
|
6
|
+
|
7
|
+
### 1.0.3
|
8
|
+
|
9
|
+
Broken on Ruby 1.9; yanked.
|
10
|
+
|
1
11
|
### 1.0.2
|
2
12
|
|
3
13
|
* Fix Ruby 1.9.3-specific bug preventing parsing most sheets [middagj, eritiro]
|
@@ -5,6 +15,7 @@
|
|
5
15
|
* You don't always have a numFmtId column, and that's OK
|
6
16
|
* Sometimes 'sharedStrings.xml' can be 'sharedstrings.xml'
|
7
17
|
* Fixed parsing times very close to 12/30/1899 [Valeriy Utyaganov]
|
18
|
+
* Be more flexible with custom formats using a numFmtId < 164
|
8
19
|
|
9
20
|
### 1.0.1
|
10
21
|
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# SimpleXlsxReader
|
1
|
+
# SimpleXlsxReader [![Build Status](https://travis-ci.org/woahdae/simple_xlsx_reader.svg?branch=master)](https://travis-ci.org/woahdae/simple_xlsx_reader)
|
2
2
|
|
3
3
|
An xlsx reader for Ruby that parses xlsx cell values into plain ruby
|
4
4
|
primitives and dates/times.
|
data/lib/simple_xlsx_reader.rb
CHANGED
@@ -19,6 +19,33 @@ end
|
|
19
19
|
module SimpleXlsxReader
|
20
20
|
class CellLoadError < StandardError; end
|
21
21
|
|
22
|
+
# We support hyperlinks as a "type" even though they're technically
|
23
|
+
# represented either as a function or an external reference in the xlsx spec.
|
24
|
+
#
|
25
|
+
# Since having hyperlink data in our sheet usually means we might want to do
|
26
|
+
# something primarily with the URL (store it in the database, download it, etc),
|
27
|
+
# we go through extra effort to parse the function or follow the reference
|
28
|
+
# to represent the hyperlink primarily as a URL. However, maybe we do want
|
29
|
+
# the hyperlink "friendly name" part (as MS calls it), so here we've subclassed
|
30
|
+
# string to tack on the friendly name. This means 80% of us that just want
|
31
|
+
# the URL value will have to do nothing extra, but the 20% that might want the
|
32
|
+
# friendly name can access it.
|
33
|
+
#
|
34
|
+
# Note, by default, the value we would get by just asking the cell would
|
35
|
+
# be the "friendly name" and *not* the URL, which is tucked away in the
|
36
|
+
# function definition or a separate "relationships" meta-document.
|
37
|
+
#
|
38
|
+
# See MS documentation on the HYPERLINK function for some background:
|
39
|
+
# https://support.office.com/en-us/article/HYPERLINK-function-333c7ce6-c5ae-4164-9c47-7de9b76f577f
|
40
|
+
class Hyperlink < String
|
41
|
+
attr_reader :friendly_name
|
42
|
+
|
43
|
+
def initialize(url, friendly_name = nil)
|
44
|
+
@friendly_name = friendly_name
|
45
|
+
super(url)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
22
49
|
def self.configuration
|
23
50
|
@configuration ||= Struct.new(:catch_cell_load_errors).new.tap do |c|
|
24
51
|
c.catch_cell_load_errors = false
|
@@ -69,29 +96,54 @@ module SimpleXlsxReader
|
|
69
96
|
##
|
70
97
|
# For internal use; stores source xml in nokogiri documents
|
71
98
|
class Xml
|
72
|
-
attr_accessor :workbook, :shared_strings, :sheets, :styles
|
99
|
+
attr_accessor :workbook, :shared_strings, :sheets, :sheet_rels, :styles
|
73
100
|
|
74
101
|
def self.load(file_path)
|
75
102
|
self.new.tap do |xml|
|
76
103
|
SimpleXlsxReader::Zip.open(file_path) do |zip|
|
77
|
-
xml.workbook = Nokogiri::XML(zip.read('xl/workbook.xml')).remove_namespaces!
|
78
|
-
xml.styles = Nokogiri::XML(zip.read('xl/styles.xml')).remove_namespaces!
|
79
|
-
|
80
|
-
# optional feature used by excel, but not often used by xlsx
|
81
|
-
# generation libraries
|
82
|
-
ss_file = (zip.to_a.map(&:name) & ['xl/sharedStrings.xml','xl/sharedstrings.xml'])[0]
|
83
|
-
if ss_file
|
84
|
-
xml.shared_strings = Nokogiri::XML(zip.read(ss_file)).remove_namespaces!
|
85
|
-
end
|
86
|
-
|
87
104
|
xml.sheets = []
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
105
|
+
xml.sheet_rels = []
|
106
|
+
|
107
|
+
# This weird style of enumerating over the entries lets us
|
108
|
+
# concisely assign entries in a case insensitive and
|
109
|
+
# slash insensitive ('/' vs '\') manner.
|
110
|
+
#
|
111
|
+
# RubyZip used to normalize the slashes, but doesn't now:
|
112
|
+
# https://github.com/rubyzip/rubyzip/issues/324
|
113
|
+
zip.entries.each do |entry|
|
114
|
+
if entry.name.match(/^xl.workbook\.xml$/) # xl/workbook.xml
|
115
|
+
xml.workbook = Nokogiri::XML(zip.read(entry)).remove_namespaces!
|
116
|
+
elsif entry.name.match(/^xl.styles\.xml$/) # xl/styles.xml
|
117
|
+
xml.styles = Nokogiri::XML(zip.read(entry)).remove_namespaces!
|
118
|
+
elsif entry.name.match(/^xl.sharedStrings\.xml$/i) # xl/sharedStrings.xml
|
119
|
+
# optional feature used by excel, but not often used by xlsx
|
120
|
+
# generation libraries. Path name is sometimes lowercase, too.
|
121
|
+
xml.shared_strings = Nokogiri::XML(zip.read(entry)).remove_namespaces!
|
122
|
+
elsif match = entry.name.match(/^xl.worksheets.sheet([0-9]*)\.xml$/)
|
123
|
+
sheet_number = match.captures.first.to_i
|
124
|
+
xml.sheets[sheet_number] =
|
125
|
+
Nokogiri::XML(zip.read(entry)).remove_namespaces!
|
126
|
+
elsif match = entry.name.match(/^xl.worksheets._rels.sheet([0-9]*)\.xml\.rels$/)
|
127
|
+
sheet_number = match.captures.first.to_i
|
128
|
+
xml.sheet_rels[sheet_number] =
|
129
|
+
Nokogiri::XML(zip.read(entry)).remove_namespaces!
|
130
|
+
end
|
131
|
+
end
|
92
132
|
|
93
|
-
|
94
|
-
|
133
|
+
# Sometimes there's a zero-index sheet.xml, ex.
|
134
|
+
# Google Docs creates:
|
135
|
+
#
|
136
|
+
# xl/worksheets/sheet.xml
|
137
|
+
# xl/worksheets/sheet1.xml
|
138
|
+
# xl/worksheets/sheet2.xml
|
139
|
+
# While Excel creates:
|
140
|
+
# xl/worksheets/sheet1.xml
|
141
|
+
# xl/worksheets/sheet2.xml
|
142
|
+
#
|
143
|
+
# So, for the latter case, let's shift [null, <Sheet 1>, <Sheet 2>]
|
144
|
+
if !xml.sheets[0]
|
145
|
+
xml.sheets.shift
|
146
|
+
xml.sheet_rels.shift
|
95
147
|
end
|
96
148
|
end
|
97
149
|
end
|
@@ -106,7 +158,7 @@ module SimpleXlsxReader
|
|
106
158
|
|
107
159
|
def load_sheets
|
108
160
|
sheet_toc.each_with_index.map do |(sheet_name, _sheet_number), i|
|
109
|
-
parse_sheet(sheet_name, xml.sheets[i]) # sheet_number is *not* the index into xml.sheets
|
161
|
+
parse_sheet(sheet_name, xml.sheets[i], xml.sheet_rels[i]) # sheet_number is *not* the index into xml.sheets
|
110
162
|
end
|
111
163
|
end
|
112
164
|
|
@@ -122,9 +174,10 @@ module SimpleXlsxReader
|
|
122
174
|
end
|
123
175
|
end
|
124
176
|
|
125
|
-
def parse_sheet(sheet_name, xsheet)
|
177
|
+
def parse_sheet(sheet_name, xsheet, xrels)
|
126
178
|
sheet = Sheet.new(sheet_name)
|
127
179
|
sheet_width, sheet_height = *sheet_dimensions(xsheet)
|
180
|
+
cells_w_links = xsheet.xpath('//hyperlinks/hyperlink').inject({}) {|acc, e| acc[e.attr(:ref)] = e.attr(:id); acc}
|
128
181
|
|
129
182
|
sheet.rows = Array.new(sheet_height) { Array.new(sheet_width) }
|
130
183
|
xsheet.xpath("/worksheet/sheetData/row/c").each do |xcell|
|
@@ -149,10 +202,21 @@ module SimpleXlsxReader
|
|
149
202
|
# by about 60%. Odd.
|
150
203
|
xvalue = type == 'inlineStr' ?
|
151
204
|
(xis = xcell.children.find {|c| c.name == 'is'}) && xis.children.find {|c| c.name == 't'} :
|
152
|
-
xcell.children.find {|c| c.name == 'v'}
|
205
|
+
xcell.children.find {|c| c.name == 'f' && c.text.start_with?('HYPERLINK(') || c.name == 'v'}
|
206
|
+
|
207
|
+
if xvalue
|
208
|
+
value = xvalue.text.strip
|
209
|
+
|
210
|
+
if rel_id = cells_w_links[xcell.attr('r')] # a hyperlink made via GUI
|
211
|
+
url = xrels.at_xpath(%(//*[@Id="#{rel_id}"])).attr('Target')
|
212
|
+
elsif xvalue.name == 'f' # only time we have a function is if it's a hyperlink
|
213
|
+
url = value.slice(/HYPERLINK\("(.*?)"/, 1)
|
214
|
+
end
|
215
|
+
end
|
153
216
|
|
154
217
|
cell = begin
|
155
|
-
self.class.cast(
|
218
|
+
self.class.cast(value, type, style,
|
219
|
+
:url => url,
|
156
220
|
:shared_strings => shared_strings,
|
157
221
|
:base_date => base_date)
|
158
222
|
rescue => e
|
@@ -328,7 +392,7 @@ module SimpleXlsxReader
|
|
328
392
|
type = style
|
329
393
|
end
|
330
394
|
|
331
|
-
case type
|
395
|
+
casted = case type
|
332
396
|
|
333
397
|
##
|
334
398
|
# There are few built-in types
|
@@ -361,7 +425,7 @@ module SimpleXlsxReader
|
|
361
425
|
# the trickiest. note that all these formats can vary on
|
362
426
|
# whether they actually contain a date, time, or datetime.
|
363
427
|
when :date, :time, :date_time
|
364
|
-
value = value
|
428
|
+
value = Float(value)
|
365
429
|
days_since_date_system_start = value.to_i
|
366
430
|
fraction_of_24 = value - days_since_date_system_start
|
367
431
|
|
@@ -388,6 +452,12 @@ module SimpleXlsxReader
|
|
388
452
|
else
|
389
453
|
value
|
390
454
|
end
|
455
|
+
|
456
|
+
if options[:url]
|
457
|
+
Hyperlink.new(options[:url], casted)
|
458
|
+
else
|
459
|
+
casted
|
460
|
+
end
|
391
461
|
end
|
392
462
|
|
393
463
|
## Returns the base_date from which to calculate dates.
|
data/simple_xlsx_reader.gemspec
CHANGED
@@ -7,19 +7,21 @@ Gem::Specification.new do |gem|
|
|
7
7
|
gem.name = "simple_xlsx_reader"
|
8
8
|
gem.version = SimpleXlsxReader::VERSION
|
9
9
|
gem.authors = ["Woody Peterson"]
|
10
|
-
gem.email = ["woody@
|
10
|
+
gem.email = ["woody.peterson@gmail.com"]
|
11
11
|
gem.description = %q{Read xlsx data the Ruby way}
|
12
12
|
gem.summary = %q{Read xlsx data the Ruby way}
|
13
13
|
gem.homepage = ""
|
14
|
+
gem.license = "MIT"
|
14
15
|
|
15
16
|
gem.add_dependency 'nokogiri'
|
16
17
|
gem.add_dependency 'rubyzip'
|
17
18
|
|
18
19
|
gem.add_development_dependency 'minitest', '>= 5.0'
|
20
|
+
gem.add_development_dependency 'rake'
|
19
21
|
gem.add_development_dependency 'pry'
|
20
22
|
|
21
23
|
gem.files = `git ls-files`.split($/)
|
22
24
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
23
|
-
gem.test_files = gem.files.grep(%r{^
|
25
|
+
gem.test_files = gem.files.grep(%r{^test/})
|
24
26
|
gem.require_paths = ["lib"]
|
25
27
|
end
|
Binary file
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require_relative 'test_helper'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
describe SimpleXlsxReader do
|
5
|
+
let(:one_sheet_file) { File.join(File.dirname(__FILE__), 'gdocs_sheet.xlsx') }
|
6
|
+
let(:subject) { SimpleXlsxReader::Document.new(one_sheet_file) }
|
7
|
+
|
8
|
+
it 'able to load file from google docs' do
|
9
|
+
subject.to_hash.must_equal({
|
10
|
+
"List 1" => [["Empty gdocs list 1"]],
|
11
|
+
"List 2" => [["Empty gdocs list 2"]]
|
12
|
+
})
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
data/test/performance_test.rb
CHANGED
@@ -96,13 +96,13 @@ describe 'SimpleXlsxReader Benchmark' do
|
|
96
96
|
bench_exp(1,10000)
|
97
97
|
end
|
98
98
|
|
99
|
-
bench_performance_linear 'parses sheets in linear time', 0.
|
99
|
+
bench_performance_linear 'parses sheets in linear time', 0.999 do |n|
|
100
100
|
|
101
101
|
raise "not enough sample data; asked for #{n}, only have #{@xml.sheets.size}"\
|
102
102
|
if @xml.sheets[n].nil?
|
103
103
|
|
104
104
|
sheet = SimpleXlsxReader::Document::Mapper.new(@xml).
|
105
|
-
parse_sheet('test', @xml.sheets[n])
|
105
|
+
parse_sheet('test', @xml.sheets[n], nil)
|
106
106
|
|
107
107
|
raise "sheet didn't parse correctly; expected #{n + 1} rows, got #{sheet.rows.size}"\
|
108
108
|
if sheet.rows.size != n + 1
|
Binary file
|
@@ -1,6 +1,8 @@
|
|
1
1
|
require_relative 'test_helper'
|
2
2
|
require 'time'
|
3
3
|
|
4
|
+
SXR = SimpleXlsxReader
|
5
|
+
|
4
6
|
describe SimpleXlsxReader do
|
5
7
|
let(:sesame_street_blog_file) { File.join(File.dirname(__FILE__),
|
6
8
|
'sesame_street_blog.xlsx') }
|
@@ -15,11 +17,11 @@ describe SimpleXlsxReader do
|
|
15
17
|
["Big Bird", "Teacher"]],
|
16
18
|
|
17
19
|
"Posts"=>
|
18
|
-
[["Author Name", "Title", "Body", "Created At", "Comment Count"],
|
19
|
-
["Big Bird", "The Number 1", "The Greatest", Time.parse("2002-01-01 11:00:00 UTC"), 1],
|
20
|
-
["Big Bird", "The Number 2", "Second Best", Time.parse("2002-01-02 14:00:00 UTC"), 2],
|
21
|
-
["Big Bird", "Formula Dates", "Tricky tricky", Time.parse("2002-01-03 14:00:00 UTC"), 0],
|
22
|
-
["Empty Eagress", nil, "The title, date, and comment have types, but no values", nil, nil]]
|
20
|
+
[["Author Name", "Title", "Body", "Created At", "Comment Count", "URL"],
|
21
|
+
["Big Bird", "The Number 1", "The Greatest", Time.parse("2002-01-01 11:00:00 UTC"), 1, SXR::Hyperlink.new("http://www.example.com/hyperlink-function", "This uses the HYPERLINK() function")],
|
22
|
+
["Big Bird", "The Number 2", "Second Best", Time.parse("2002-01-02 14:00:00 UTC"), 2, SXR::Hyperlink.new("http://www.example.com/hyperlink-gui", "This uses the hyperlink GUI option")],
|
23
|
+
["Big Bird", "Formula Dates", "Tricky tricky", Time.parse("2002-01-03 14:00:00 UTC"), 0, nil],
|
24
|
+
["Empty Eagress", nil, "The title, date, and comment have types, but no values", nil, nil, nil]]
|
23
25
|
})
|
24
26
|
end
|
25
27
|
end
|
@@ -63,10 +65,33 @@ describe SimpleXlsxReader do
|
|
63
65
|
must_equal Time.parse('2013-08-19 18:30 UTC')
|
64
66
|
end
|
65
67
|
|
68
|
+
it 'reads less-than-zero complex number types styled as times' do
|
69
|
+
described_class.cast('6.25E-2', 'n', :time).
|
70
|
+
must_equal Time.parse('1899-12-30 01:30:00 UTC')
|
71
|
+
end
|
72
|
+
|
66
73
|
it 'reads number types styled as date_times' do
|
67
74
|
described_class.cast('41505.77083', 'n', :date_time).
|
68
75
|
must_equal Time.parse('2013-08-19 18:30 UTC')
|
69
76
|
end
|
77
|
+
|
78
|
+
it 'raises when date-styled values are not numerical' do
|
79
|
+
lambda { described_class.cast('14 is not a valid date', nil, :date) }.
|
80
|
+
must_raise(ArgumentError)
|
81
|
+
end
|
82
|
+
|
83
|
+
describe "with the url option" do
|
84
|
+
let(:url) { "http://www.example.com/hyperlink" }
|
85
|
+
it 'creates a hyperlink with a string type' do
|
86
|
+
described_class.cast("A link", 'str', :string, url: url).
|
87
|
+
must_equal SXR::Hyperlink.new(url, "A link")
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'creates a hyperlink with a shared string type' do
|
91
|
+
described_class.cast("2", 's', nil, shared_strings: ['a','b','c'], url: url).
|
92
|
+
must_equal SXR::Hyperlink.new(url, 'c')
|
93
|
+
end
|
94
|
+
end
|
70
95
|
end
|
71
96
|
|
72
97
|
describe '#shared_strings' do
|
@@ -253,15 +278,15 @@ describe SimpleXlsxReader do
|
|
253
278
|
it 'raises if configuration.catch_cell_load_errors' do
|
254
279
|
SimpleXlsxReader.configuration.catch_cell_load_errors = false
|
255
280
|
|
256
|
-
lambda { described_class.new(xml).parse_sheet('test', xml.sheets.first) }.
|
281
|
+
lambda { described_class.new(xml).parse_sheet('test', xml.sheets.first, nil) }.
|
257
282
|
must_raise(SimpleXlsxReader::CellLoadError)
|
258
283
|
end
|
259
284
|
|
260
285
|
it 'records a load error if not configuration.catch_cell_load_errors' do
|
261
286
|
SimpleXlsxReader.configuration.catch_cell_load_errors = true
|
262
287
|
|
263
|
-
sheet = described_class.new(xml).parse_sheet('test', xml.sheets.first)
|
264
|
-
sheet.load_errors[[0,0]].must_include 'invalid value for
|
288
|
+
sheet = described_class.new(xml).parse_sheet('test', xml.sheets.first, nil)
|
289
|
+
sheet.load_errors[[0,0]].must_include 'invalid value for Float'
|
265
290
|
end
|
266
291
|
end
|
267
292
|
|
@@ -295,7 +320,7 @@ describe SimpleXlsxReader do
|
|
295
320
|
end
|
296
321
|
|
297
322
|
before do
|
298
|
-
@row = described_class.new(xml).parse_sheet('test', xml.sheets.first).rows[0]
|
323
|
+
@row = described_class.new(xml).parse_sheet('test', xml.sheets.first, nil).rows[0]
|
299
324
|
end
|
300
325
|
|
301
326
|
it 'continues even when cells are missing numFmtId attributes ' do
|
@@ -330,8 +355,21 @@ describe SimpleXlsxReader do
|
|
330
355
|
<c r='G1' t='inlineStr' s='0'>
|
331
356
|
<is><t>Cell G1</t></is>
|
332
357
|
</c>
|
358
|
+
|
359
|
+
<c r='H1' s='0'>
|
360
|
+
<f>HYPERLINK("http://www.example.com/hyperlink-function", "HYPERLINK function")</f>
|
361
|
+
<v>HYPERLINK function</v>
|
362
|
+
</c>
|
363
|
+
|
364
|
+
<c r='I1' s='0'>
|
365
|
+
<v>GUI-made hyperlink</v>
|
366
|
+
</c>
|
333
367
|
</row>
|
334
368
|
</sheetData>
|
369
|
+
|
370
|
+
<hyperlinks>
|
371
|
+
<hyperlink ref="I1" id="rId1"/>
|
372
|
+
</hyperlinks>
|
335
373
|
</worksheet>
|
336
374
|
XML
|
337
375
|
).remove_namespaces!]
|
@@ -349,11 +387,28 @@ describe SimpleXlsxReader do
|
|
349
387
|
</styleSheet>
|
350
388
|
XML
|
351
389
|
).remove_namespaces!
|
390
|
+
|
391
|
+
# Although not a "type" or "style" according to xlsx spec,
|
392
|
+
# it sure could/should be, so let's test it with the rest of our
|
393
|
+
# typecasting code.
|
394
|
+
xml.sheet_rels = [Nokogiri::XML(
|
395
|
+
<<-XML
|
396
|
+
<Relationships>
|
397
|
+
<Relationship
|
398
|
+
Id="rId1"
|
399
|
+
Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink"
|
400
|
+
Target="http://www.example.com/hyperlink-gui"
|
401
|
+
TargetMode="External"
|
402
|
+
/>
|
403
|
+
</Relationships>
|
404
|
+
XML
|
405
|
+
).remove_namespaces!]
|
406
|
+
|
352
407
|
end
|
353
408
|
end
|
354
409
|
|
355
410
|
before do
|
356
|
-
@row = described_class.new(xml).parse_sheet('test', xml.sheets.first).rows[0]
|
411
|
+
@row = described_class.new(xml).parse_sheet('test', xml.sheets.first, xml.sheet_rels.first).rows[0]
|
357
412
|
end
|
358
413
|
|
359
414
|
it "reads 'Generic' cells as strings" do
|
@@ -387,6 +442,18 @@ describe SimpleXlsxReader do
|
|
387
442
|
it "reads strings formatted as inlineStr" do
|
388
443
|
@row[6].must_equal 'Cell G1'
|
389
444
|
end
|
445
|
+
|
446
|
+
it "reads hyperlinks created via HYPERLINK()" do
|
447
|
+
@row[7].must_equal(
|
448
|
+
SXR::Hyperlink.new(
|
449
|
+
"http://www.example.com/hyperlink-function", "HYPERLINK function"))
|
450
|
+
end
|
451
|
+
|
452
|
+
it "reads hyperlinks created via the GUI" do
|
453
|
+
@row[8].must_equal(
|
454
|
+
SXR::Hyperlink.new(
|
455
|
+
"http://www.example.com/hyperlink-gui", "GUI-made hyperlink"))
|
456
|
+
end
|
390
457
|
end
|
391
458
|
|
392
459
|
describe 'parsing documents with blank rows' do
|
@@ -435,7 +502,7 @@ describe SimpleXlsxReader do
|
|
435
502
|
end
|
436
503
|
|
437
504
|
before do
|
438
|
-
@rows = described_class.new(xml).parse_sheet('test', xml.sheets.first).rows
|
505
|
+
@rows = described_class.new(xml).parse_sheet('test', xml.sheets.first, nil).rows
|
439
506
|
end
|
440
507
|
|
441
508
|
it "reads row data despite gaps in row numbering" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_xlsx_reader
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Woody Peterson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-07-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: pry
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -68,12 +82,13 @@ dependencies:
|
|
68
82
|
version: '0'
|
69
83
|
description: Read xlsx data the Ruby way
|
70
84
|
email:
|
71
|
-
- woody@
|
85
|
+
- woody.peterson@gmail.com
|
72
86
|
executables: []
|
73
87
|
extensions: []
|
74
88
|
extra_rdoc_files: []
|
75
89
|
files:
|
76
90
|
- ".gitignore"
|
91
|
+
- ".travis.yml"
|
77
92
|
- CHANGELOG.md
|
78
93
|
- Gemfile
|
79
94
|
- LICENSE.txt
|
@@ -86,6 +101,8 @@ files:
|
|
86
101
|
- test/date1904_test.rb
|
87
102
|
- test/datetime_test.rb
|
88
103
|
- test/datetimes.xlsx
|
104
|
+
- test/gdocs_sheet.xlsx
|
105
|
+
- test/gdocs_sheet_test.rb
|
89
106
|
- test/lower_case_sharedstrings.xlsx
|
90
107
|
- test/lower_case_sharedstrings_test.rb
|
91
108
|
- test/performance_test.rb
|
@@ -95,7 +112,8 @@ files:
|
|
95
112
|
- test/styles.xml
|
96
113
|
- test/test_helper.rb
|
97
114
|
homepage: ''
|
98
|
-
licenses:
|
115
|
+
licenses:
|
116
|
+
- MIT
|
99
117
|
metadata: {}
|
100
118
|
post_install_message:
|
101
119
|
rdoc_options: []
|
@@ -113,7 +131,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
131
|
version: '0'
|
114
132
|
requirements: []
|
115
133
|
rubyforge_project:
|
116
|
-
rubygems_version: 2.
|
134
|
+
rubygems_version: 2.5.2
|
117
135
|
signing_key:
|
118
136
|
specification_version: 4
|
119
137
|
summary: Read xlsx data the Ruby way
|
@@ -122,6 +140,8 @@ test_files:
|
|
122
140
|
- test/date1904_test.rb
|
123
141
|
- test/datetime_test.rb
|
124
142
|
- test/datetimes.xlsx
|
143
|
+
- test/gdocs_sheet.xlsx
|
144
|
+
- test/gdocs_sheet_test.rb
|
125
145
|
- test/lower_case_sharedstrings.xlsx
|
126
146
|
- test/lower_case_sharedstrings_test.rb
|
127
147
|
- test/performance_test.rb
|