rubyXL 1.1.12 → 1.2.0
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.
- data/VERSION +1 -1
- data/lib/rubyXL/parser.rb +102 -82
- data/lib/rubyXL/workbook.rb +7 -4
- data/lib/rubyXL/worksheet.rb +7 -3
- data/rubyXL.gemspec +2 -2
- data/spec/lib/cell_spec.rb +11 -2
- metadata +15 -5
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.2.0
|
data/lib/rubyXL/parser.rb
CHANGED
@@ -155,116 +155,136 @@ module RubyXL
|
|
155
155
|
wb.worksheets[i] = Parser.create_matrix(wb, i, files)
|
156
156
|
j = i+1
|
157
157
|
|
158
|
+
namespaces = files[j].root.namespaces()
|
158
159
|
unless @data_only
|
159
|
-
|
160
|
-
|
161
|
-
wb.worksheets[i].sheet_view = hash[:sheetViews][:sheetView]
|
160
|
+
sheet_views_node= files[j].xpath('/xmlns:worksheet/xmlns:sheetViews[xmlns:sheetView]',namespaces).first
|
161
|
+
wb.worksheets[i].sheet_view = Hash.xml_node_to_hash(sheet_views_node)[:sheetView]
|
162
162
|
|
163
163
|
##col styles##
|
164
|
-
|
165
|
-
unless
|
166
|
-
wb.worksheets[i].cols=
|
164
|
+
cols_node_set = files[j].xpath('/xmlns:worksheet/xmlns:cols/xmlns:col',namespaces)
|
165
|
+
unless cols_node_set.empty?
|
166
|
+
wb.worksheets[i].cols= cols_node_set.map(&:attributes)
|
167
167
|
end
|
168
168
|
##end col styles##
|
169
169
|
|
170
170
|
##merge_cells##
|
171
|
-
|
172
|
-
unless
|
173
|
-
wb.worksheets[i].merged_cells =
|
171
|
+
merge_cells_node = files[j].xpath('/xmlns:worksheet/xmlns:mergeCells[xmlns:mergeCell]',namespaces)
|
172
|
+
unless merge_cells_node.empty?
|
173
|
+
wb.worksheets[i].merged_cells = Hash.xml_node_to_hash(merge_cells_node.first)[:mergeCell]
|
174
174
|
end
|
175
175
|
##end merge_cells##
|
176
176
|
|
177
177
|
##sheet_view pane##
|
178
|
-
pane_data =
|
178
|
+
pane_data = wb.worksheets[i].sheet_view[:pane]
|
179
179
|
wb.worksheets[i].pane = pane_data
|
180
180
|
##end sheet_view pane##
|
181
181
|
|
182
182
|
##data_validation##
|
183
|
-
|
184
|
-
unless
|
185
|
-
|
183
|
+
data_validations_node = files[j].xpath('/xmlns:worksheet/xmlns:dataValidations[xmlns:dataValidation]',namespaces)
|
184
|
+
unless data_validations_node.empty?
|
185
|
+
wb.worksheets[i].validations = Hash.xml_node_to_hash(data_validations_node.first)[:dataValidation]
|
186
|
+
else
|
187
|
+
wb.worksheets[i].validations=nil
|
186
188
|
end
|
187
|
-
wb.worksheets[i].validations = data_validation
|
188
189
|
##end data_validation##
|
189
190
|
|
190
191
|
#extLst
|
191
|
-
|
192
|
+
ext_list_node=files[j].xpath('/xmlns:worksheet/xmlns:extLst',namespaces)
|
193
|
+
unless ext_list_node.empty?
|
194
|
+
wb.worksheets[i].extLst = Hash.xml_node_to_hash(ext_list_node.first)
|
195
|
+
else
|
196
|
+
wb.worksheets[i].extLst=nil
|
197
|
+
end
|
192
198
|
#extLst
|
193
199
|
|
194
200
|
##legacy drawing##
|
195
|
-
|
196
|
-
|
201
|
+
legacy_drawing_node = files[j].xpath('/xmlns:worksheet/xmlns:legacyDrawing',namespaces)
|
202
|
+
unless legacy_drawing_node.empty?
|
203
|
+
wb.worksheets[i].legacy_drawing = Hash.xml_node_to_hash(legacy_drawing_node.first)
|
204
|
+
else
|
205
|
+
wb.worksheets[i].legacy_drawing = nil
|
206
|
+
end
|
197
207
|
##end legacy drawing
|
198
208
|
end
|
199
209
|
|
200
|
-
|
210
|
+
|
211
|
+
row_data = files[j].xpath('/xmlns:worksheet/xmlns:sheetData/xmlns:row[xmlns:c[xmlns:v]]',namespaces)
|
212
|
+
row_data.each do |row|
|
213
|
+
unless @data_only
|
214
|
+
##row styles##
|
215
|
+
row_style = '0'
|
216
|
+
row_attributes = row.attributes
|
217
|
+
unless row_attributes['s'].nil?
|
218
|
+
row_style = row_attributes['s'].value
|
219
|
+
end
|
201
220
|
|
202
|
-
|
203
|
-
row_data.each do |row|
|
221
|
+
wb.worksheets[i].row_styles[row_attributes['r'].content] = { :style => row_style }
|
204
222
|
|
205
|
-
unless
|
206
|
-
|
207
|
-
|
208
|
-
row_style = '0'
|
209
|
-
unless row.attribute('s').nil?
|
210
|
-
row_style = row.attribute('s').value.to_s
|
211
|
-
end
|
212
|
-
|
213
|
-
wb.worksheets[i].row_styles[row.attribute('r').to_s] = { :style => row_style.to_s }
|
214
|
-
|
215
|
-
unless row.attribute('ht').to_s == ""
|
216
|
-
wb.worksheets[i].change_row_height(Integer(row.attribute('r').to_s)-1,
|
217
|
-
Float(row.attribute('ht').to_s))
|
218
|
-
end
|
219
|
-
end
|
220
|
-
##end row styles##
|
223
|
+
unless row_attributes['ht'].content == ""
|
224
|
+
wb.worksheets[i].change_row_height(Integer(row_attributes['r'].content)-1,
|
225
|
+
Float(row_attributes['ht'].content))
|
221
226
|
end
|
227
|
+
##end row styles##
|
228
|
+
end
|
222
229
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
data_type = value.attribute('t').to_s
|
229
|
-
|
230
|
-
if (value.css('v').to_s == "") || (value.css('v').children.to_s == "") #no data
|
231
|
-
cell_data = nil
|
232
|
-
elsif data_type == 's' #shared string
|
233
|
-
str_index = Integer(value.css('v').children.to_s)
|
234
|
-
cell_data = shared_strings[str_index].to_s
|
235
|
-
elsif data_type=='str' #raw string
|
236
|
-
cell_data = value.css('v').children.to_s
|
237
|
-
elsif data_type=='e' #error
|
238
|
-
cell_data = value.css('v').children.to_s
|
239
|
-
else# (value.css('v').to_s != "") && (value.css('v').children.to_s != "") #is number
|
240
|
-
data_type = ''
|
241
|
-
if(value.css('v').children.to_s =~ /\./) #is float
|
242
|
-
cell_data = Float(value.css('v').children.to_s)
|
243
|
-
else
|
244
|
-
cell_data = Integer(value.css('v').children.to_s)
|
245
|
-
end
|
246
|
-
end
|
247
|
-
cell_formula = nil
|
248
|
-
fmla_css = value.css('f')
|
249
|
-
if(fmla_css.to_s != "")
|
250
|
-
cell_formula = fmla_css.children.to_s
|
251
|
-
cell_formula_attr = {}
|
252
|
-
cell_formula_attr['t'] = fmla_css.attribute('t').to_s if fmla_css.attribute('t')
|
253
|
-
cell_formula_attr['ref'] = fmla_css.attribute('ref').to_s if fmla_css.attribute('ref')
|
254
|
-
cell_formula_attr['si'] = fmla_css.attribute('si').to_s if fmla_css.attribute('si')
|
255
|
-
end
|
230
|
+
c_row = row.search('./xmlns:c[xmlns:v]')
|
231
|
+
c_row.each do |value|
|
232
|
+
value_attributes= value.attributes
|
233
|
+
cell_index = Parser.convert_to_index(value_attributes['r'].content)
|
234
|
+
style_index = nil
|
256
235
|
|
257
|
-
|
258
|
-
|
236
|
+
data_type = value_attributes['t'].content if value_attributes['t']
|
237
|
+
element_hash ={}
|
238
|
+
value.children.each do |node|
|
239
|
+
element_hash["#{node.name()}_element"]=node
|
240
|
+
end
|
241
|
+
# v is the value element that is part of the cell
|
242
|
+
if element_hash["v_element"]
|
243
|
+
v_element_content = element_hash["v_element"].content
|
244
|
+
else
|
245
|
+
v_element_content=""
|
246
|
+
end
|
247
|
+
if v_element_content =="" #no data
|
248
|
+
cell_data = nil
|
249
|
+
elsif data_type == 's' #shared string
|
250
|
+
str_index = Integer(v_element_content)
|
251
|
+
cell_data = shared_strings[str_index].to_s
|
252
|
+
elsif data_type=='str' #raw string
|
253
|
+
cell_data = v_element_content
|
254
|
+
elsif data_type=='e' #error
|
255
|
+
cell_data = v_element_content
|
256
|
+
else# (value.css('v').to_s != "") && (value.css('v').children.to_s != "") #is number
|
257
|
+
data_type = ''
|
258
|
+
if(v_element_content =~ /\./) #is float
|
259
|
+
cell_data = Float(v_element_content)
|
259
260
|
else
|
260
|
-
|
261
|
+
cell_data = Integer(v_element_content)
|
261
262
|
end
|
263
|
+
end
|
264
|
+
cell_formula = nil
|
265
|
+
fmla_css = element_hash["f_element"]
|
266
|
+
if fmla_css && fmla_css.content
|
267
|
+
fmla_css_content= fmla_css.content
|
268
|
+
if(fmla_css_content != "")
|
269
|
+
cell_formula = fmla_css_content
|
270
|
+
cell_formula_attr = {}
|
271
|
+
fmla_css_attributes = fmla_css.attributes
|
272
|
+
cell_formula_attr['t'] = fmla_css_attributes['t'].content if fmla_css_attributes['t']
|
273
|
+
cell_formula_attr['ref'] = fmla_css_attributes['ref'].content if fmla_css_attributes['ref']
|
274
|
+
cell_formula_attr['si'] = fmla_css_attributes['si'].content if fmla_css_attributes['si']
|
275
|
+
end
|
276
|
+
end
|
262
277
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
278
|
+
unless @data_only
|
279
|
+
style_index = value['s'].to_i #nil goes to 0 (default)
|
280
|
+
else
|
281
|
+
style_index = 0
|
267
282
|
end
|
283
|
+
|
284
|
+
wb.worksheets[i].sheet_data[cell_index[0]][cell_index[1]] =
|
285
|
+
Cell.new(wb.worksheets[i],cell_index[0],cell_index[1],cell_data,cell_formula,
|
286
|
+
data_type,style_index,cell_formula_attr)
|
287
|
+
cell = wb.worksheets[i].sheet_data[cell_index[0]][cell_index[1]]
|
268
288
|
end
|
269
289
|
end
|
270
290
|
end
|
@@ -288,13 +308,13 @@ module RubyXL
|
|
288
308
|
|
289
309
|
files = Hash.new
|
290
310
|
|
291
|
-
files['app'] = Nokogiri::XML.parse(File.
|
292
|
-
files['core'] = Nokogiri::XML.parse(File.
|
311
|
+
files['app'] = Nokogiri::XML.parse(File.open(File.join(dir_path,'docProps','app.xml'),'r'))
|
312
|
+
files['core'] = Nokogiri::XML.parse(File.open(File.join(dir_path,'docProps','core.xml'),'r'))
|
293
313
|
|
294
|
-
files['workbook'] = Nokogiri::XML.parse(File.
|
314
|
+
files['workbook'] = Nokogiri::XML.parse(File.open(File.join(dir_path,'xl','workbook.xml'),'r'))
|
295
315
|
|
296
316
|
if(File.exist?(File.join(dir_path,'xl','sharedStrings.xml')))
|
297
|
-
files['sharedString'] = Nokogiri::XML.parse(File.
|
317
|
+
files['sharedString'] = Nokogiri::XML.parse(File.open(File.join(dir_path,'xl','sharedStrings.xml'),'r'))
|
298
318
|
end
|
299
319
|
|
300
320
|
unless @data_only
|
@@ -352,7 +372,7 @@ module RubyXL
|
|
352
372
|
files['vbaProject'] = File.open(File.join(dir_path,"xl","vbaProject.bin"),'rb').read
|
353
373
|
end
|
354
374
|
|
355
|
-
files['styles'] = Nokogiri::XML.parse(File.
|
375
|
+
files['styles'] = Nokogiri::XML.parse(File.open(File.join(dir_path,'xl','styles.xml'),'r'))
|
356
376
|
end
|
357
377
|
|
358
378
|
@num_sheets = files['workbook'].css('sheets').children.size
|
@@ -362,7 +382,7 @@ module RubyXL
|
|
362
382
|
i=1
|
363
383
|
1.upto(@num_sheets) do
|
364
384
|
filename = 'sheet'+i.to_s
|
365
|
-
files[i] = Nokogiri::XML.parse(File.
|
385
|
+
files[i] = Nokogiri::XML.parse(File.open(File.join(dir_path,'xl','worksheets',filename+'.xml'),'r'))
|
366
386
|
i=i+1
|
367
387
|
end
|
368
388
|
|
data/lib/rubyXL/workbook.rb
CHANGED
@@ -47,7 +47,7 @@ module RubyXL
|
|
47
47
|
@calc_chain = nil #unnecessary?
|
48
48
|
@num_strings = 0 #num strings total
|
49
49
|
@size = 0 #num strings in shared_strings array
|
50
|
-
@date1904 = date1904
|
50
|
+
@date1904 = date1904 > 0
|
51
51
|
@external_links = nil
|
52
52
|
@style_corrector = nil
|
53
53
|
@drawings = nil
|
@@ -78,6 +78,9 @@ module RubyXL
|
|
78
78
|
#filepath of xlsx file (including file itself)
|
79
79
|
def write(filepath=@filepath)
|
80
80
|
validate_before_write
|
81
|
+
if !(filepath =~ /(.+)\.xls(x|m)/)
|
82
|
+
raise "Only xlsx and xlsm files are supported. Unsupported type for file: #{filepath}"
|
83
|
+
end
|
81
84
|
dirpath = ''
|
82
85
|
extension = 'xls'
|
83
86
|
if(filepath =~ /((.|\s)*)\.xls(x|m)$/)
|
@@ -197,7 +200,7 @@ module RubyXL
|
|
197
200
|
compare_date = DateTime.parse('December 31, 1899')
|
198
201
|
end
|
199
202
|
# add one day to compare date for erroneous 1900 leap year compatibility
|
200
|
-
date.ajd -
|
203
|
+
date.ajd + 1 - compare_date.ajd
|
201
204
|
end
|
202
205
|
|
203
206
|
def num_to_date(num)
|
@@ -207,8 +210,8 @@ module RubyXL
|
|
207
210
|
else
|
208
211
|
compare_date = DateTime.parse('December 31, 1899')
|
209
212
|
end
|
210
|
-
#
|
211
|
-
compare_date
|
213
|
+
# subtract one day to compare date for erroneous 1900 leap year compatibility
|
214
|
+
compare_date - 1 + num
|
212
215
|
end
|
213
216
|
|
214
217
|
#gets style object from style array given index
|
data/lib/rubyXL/worksheet.rb
CHANGED
@@ -60,8 +60,11 @@ class Worksheet < PrivateClass
|
|
60
60
|
|
61
61
|
# makes array of hashes in table_hash[:table]
|
62
62
|
# as well as hash of arrays in table_hash[header]
|
63
|
-
|
64
|
-
|
63
|
+
table_index = current_row - original_row
|
64
|
+
cell_test= (!cell.nil? && !cell.value.nil?)
|
65
|
+
while cell_test || !table_hash[:table][table_index].empty?
|
66
|
+
|
67
|
+
table_hash[header] << (cell_test ? cell.value : nil)
|
65
68
|
|
66
69
|
table_index = current_row - original_row
|
67
70
|
|
@@ -69,7 +72,7 @@ class Worksheet < PrivateClass
|
|
69
72
|
table_hash[:table][table_index] = {}
|
70
73
|
end
|
71
74
|
|
72
|
-
table_hash[:table][table_index][header] = cell.value
|
75
|
+
table_hash[:table][table_index][header] = cell.value if cell_test
|
73
76
|
|
74
77
|
current_row += 1
|
75
78
|
if @sheet_data[current_row].nil?
|
@@ -77,6 +80,7 @@ class Worksheet < PrivateClass
|
|
77
80
|
else
|
78
81
|
cell = @sheet_data[current_row][index]
|
79
82
|
end
|
83
|
+
cell_test= (!cell.nil? && !cell.value.nil?)
|
80
84
|
end
|
81
85
|
end
|
82
86
|
|
data/rubyXL.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{rubyXL}
|
8
|
-
s.version = "1.
|
8
|
+
s.version = "1.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Vivek Bhagwat"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2012-01-06}
|
13
13
|
s.description = %q{rubyXL is a gem which allows the parsing, creation, and manipulation of Microsoft Excel (.xlsx/.xlsm) Documents}
|
14
14
|
s.email = %q{bhagwat.vivek@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
data/spec/lib/cell_spec.rb
CHANGED
@@ -196,9 +196,18 @@ describe RubyXL::Cell do
|
|
196
196
|
it 'should return the value of a date' do
|
197
197
|
date = Date.parse('January 1, 2011')
|
198
198
|
@cell.change_contents(date)
|
199
|
-
@cell.should_receive(:is_date?).
|
199
|
+
@cell.should_receive(:is_date?).any_number_of_times.and_return(true)
|
200
200
|
@cell.value.should == date
|
201
201
|
end
|
202
|
+
|
203
|
+
it 'should convert date numbers correctly' do
|
204
|
+
date = 41019
|
205
|
+
@cell.change_contents(date)
|
206
|
+
@cell.should_receive(:is_date?).any_number_of_times.and_return(true)
|
207
|
+
puts @cell.value
|
208
|
+
puts Date.parse('April 20, 2012')
|
209
|
+
@cell.value.should == Date.parse('April 20, 2012')
|
210
|
+
end
|
202
211
|
end
|
203
212
|
|
204
213
|
describe '.change_contents' do
|
@@ -211,7 +220,7 @@ describe RubyXL::Cell do
|
|
211
220
|
it 'should cause cell value to match a date that is passed in' do
|
212
221
|
date = Date.parse('January 1, 2011')
|
213
222
|
@cell.change_contents(date)
|
214
|
-
@cell.should_receive(:is_date?).
|
223
|
+
@cell.should_receive(:is_date?).any_number_of_times.and_return(true)
|
215
224
|
@cell.value.should == date
|
216
225
|
@cell.formula.should == nil
|
217
226
|
end
|
metadata
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubyXL
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 31
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
-
|
10
|
-
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
segments_generated: true
|
11
|
+
version: 1.2.0
|
11
12
|
platform: ruby
|
12
13
|
authors:
|
13
14
|
- Vivek Bhagwat
|
@@ -15,7 +16,7 @@ autorequire:
|
|
15
16
|
bindir: bin
|
16
17
|
cert_chain: []
|
17
18
|
|
18
|
-
date:
|
19
|
+
date: 2012-01-06 00:00:00 -05:00
|
19
20
|
default_executable:
|
20
21
|
dependencies:
|
21
22
|
- !ruby/object:Gem::Dependency
|
@@ -28,6 +29,7 @@ dependencies:
|
|
28
29
|
hash: 3
|
29
30
|
segments:
|
30
31
|
- 0
|
32
|
+
segments_generated: true
|
31
33
|
version: "0"
|
32
34
|
name: shoulda
|
33
35
|
requirement: *id001
|
@@ -44,6 +46,7 @@ dependencies:
|
|
44
46
|
- 1
|
45
47
|
- 0
|
46
48
|
- 0
|
49
|
+
segments_generated: true
|
47
50
|
version: 1.0.0
|
48
51
|
name: bundler
|
49
52
|
requirement: *id002
|
@@ -60,6 +63,7 @@ dependencies:
|
|
60
63
|
- 1
|
61
64
|
- 6
|
62
65
|
- 0
|
66
|
+
segments_generated: true
|
63
67
|
version: 1.6.0
|
64
68
|
name: jeweler
|
65
69
|
requirement: *id003
|
@@ -74,6 +78,7 @@ dependencies:
|
|
74
78
|
hash: 3
|
75
79
|
segments:
|
76
80
|
- 0
|
81
|
+
segments_generated: true
|
77
82
|
version: "0"
|
78
83
|
name: rcov
|
79
84
|
requirement: *id004
|
@@ -90,6 +95,7 @@ dependencies:
|
|
90
95
|
- 1
|
91
96
|
- 4
|
92
97
|
- 4
|
98
|
+
segments_generated: true
|
93
99
|
version: 1.4.4
|
94
100
|
name: nokogiri
|
95
101
|
requirement: *id005
|
@@ -106,6 +112,7 @@ dependencies:
|
|
106
112
|
- 0
|
107
113
|
- 9
|
108
114
|
- 4
|
115
|
+
segments_generated: true
|
109
116
|
version: 0.9.4
|
110
117
|
name: rubyzip
|
111
118
|
requirement: *id006
|
@@ -122,6 +129,7 @@ dependencies:
|
|
122
129
|
- 1
|
123
130
|
- 3
|
124
131
|
- 4
|
132
|
+
segments_generated: true
|
125
133
|
version: 1.3.4
|
126
134
|
name: rspec
|
127
135
|
requirement: *id007
|
@@ -187,6 +195,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
187
195
|
hash: 3
|
188
196
|
segments:
|
189
197
|
- 0
|
198
|
+
segments_generated: true
|
190
199
|
version: "0"
|
191
200
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
192
201
|
none: false
|
@@ -196,6 +205,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
196
205
|
hash: 3
|
197
206
|
segments:
|
198
207
|
- 0
|
208
|
+
segments_generated: true
|
199
209
|
version: "0"
|
200
210
|
requirements: []
|
201
211
|
|