simple_xlsx_reader 1.0.2 → 1.0.4
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.
- 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 [](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
|