workbook 0.4.9 → 0.4.10
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 +1 -1
- data/README.md +4 -5
- data/lib/workbook.rb +2 -0
- data/lib/workbook/cell.rb +2 -211
- data/lib/workbook/column.rb +31 -1
- data/lib/workbook/modules/cell.rb +279 -0
- data/lib/workbook/modules/raw_objects_storage.rb +3 -2
- data/lib/workbook/readers/xlsx_reader.rb +12 -45
- data/lib/workbook/row.rb +13 -5
- data/lib/workbook/sheet.rb +3 -0
- data/lib/workbook/table.rb +14 -0
- data/lib/workbook/template.rb +5 -0
- data/lib/workbook/types/Date.rb +14 -2
- data/lib/workbook/types/FalseClass.rb +1 -3
- data/lib/workbook/version.rb +1 -1
- data/lib/workbook/writers/xlsx_writer.rb +60 -30
- data/test/test_column.rb +30 -0
- data/test/{test_cell.rb → test_modules_cell.rb} +8 -1
- data/test/test_readers_xlsx_reader.rb +1 -7
- data/test/test_row.rb +1 -1
- data/test/test_sheet.rb +1 -0
- data/test/test_table.rb +14 -3
- data/test/test_template.rb +5 -0
- data/test/test_types_date.rb +11 -0
- data/test/test_writers_xlsx_writer.rb +40 -85
- data/workbook.gemspec +5 -2
- metadata +24 -7
@@ -5,8 +5,9 @@ module Workbook
|
|
5
5
|
module RawObjectsStorage
|
6
6
|
|
7
7
|
# A raw is a 'raw' object, representing a workbook, or cell, or whatever... in a particular format (defined by its class)
|
8
|
-
def add_raw raw_object
|
9
|
-
|
8
|
+
def add_raw raw_object, options={}
|
9
|
+
class_of_obj = options[:raw_object_class] ? options[:raw_object_class] : raw_object.class
|
10
|
+
raws[class_of_obj]=raw_object
|
10
11
|
end
|
11
12
|
|
12
13
|
# Returns true if there is a template for a certain class, otherwise false
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
|
-
require '
|
2
|
+
require 'roo'
|
3
3
|
require 'workbook/readers/xls_shared'
|
4
4
|
|
5
5
|
|
@@ -16,55 +16,22 @@ module Workbook
|
|
16
16
|
end
|
17
17
|
def load_xlsx file_obj
|
18
18
|
file_obj = file_obj.path if file_obj.is_a? File
|
19
|
-
|
20
|
-
|
19
|
+
# file_obj = file_obj.match(/^\/(.*)/) ? file_obj : "./#{file_obj}"
|
20
|
+
# p "opening #{file_obj}"
|
21
|
+
sp = Roo::Excelx.new(file_obj)
|
22
|
+
template.add_raw sp, raw_object_class: Roo::Spreadsheet
|
21
23
|
parse_xlsx sp
|
22
24
|
end
|
23
25
|
|
24
|
-
def parse_xlsx xlsx_spreadsheet=template.raws[
|
26
|
+
def parse_xlsx xlsx_spreadsheet=template.raws[Roo::Spreadsheet], options={}
|
25
27
|
options = {:additional_type_parsing=>false}.merge options
|
26
|
-
|
27
|
-
xlsx_spreadsheet.
|
28
|
-
s = create_or_open_sheet_at(
|
29
|
-
|
30
|
-
|
31
|
-
col_widths = xlsx_spreadsheet.worksheets.first.cols.collect{|a| a[:attributes][:width].to_f if a[:attributes]}
|
32
|
-
rescue
|
33
|
-
# Column widths couldn't be read, no big deal...
|
34
|
-
end
|
35
|
-
|
36
|
-
worksheet.each_with_index do |row, ri|
|
37
|
-
if row
|
38
|
-
r = s.table.create_or_open_row_at(ri)
|
39
|
-
row.cells.each_with_index do |cell,ci|
|
40
|
-
if cell.nil?
|
41
|
-
r[ci] = Workbook::Cell.new nil
|
42
|
-
else
|
43
|
-
r[ci] = Workbook::Cell.new cell.value
|
44
|
-
r[ci].parse!
|
45
|
-
xls_format = cell.style_index
|
46
|
-
col_width = nil
|
47
|
-
|
48
|
-
if ri == 0
|
49
|
-
col_width = col_widths[ci]
|
50
|
-
end
|
51
|
-
f = template.create_or_find_format_by "style_index_#{cell.style_index}", col_width
|
52
|
-
f[:width]= col_width
|
53
|
-
background_color = cell.fill_color
|
54
|
-
background_color = (background_color.length == 8) ? background_color[2..8] : background_color #ignoring alpha for now.
|
55
|
-
f[:background_color] = "##{background_color}"
|
56
|
-
|
57
|
-
f[:number_format] = ms_formatting_to_strftime(cell.number_format)
|
58
|
-
# f[:font_family] = cell.font_name
|
59
|
-
# f[:color] = "##{cell.font_color}"
|
60
|
-
|
61
|
-
f.add_raw xls_format
|
62
|
-
|
63
|
-
r[ci].format = f
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
28
|
+
sheet_index = 0
|
29
|
+
xlsx_spreadsheet.each_with_pagename do |sheet_name, sheet|
|
30
|
+
s = create_or_open_sheet_at(sheet_index)
|
31
|
+
sheet.each_with_index do |row, rowi|
|
32
|
+
s.table << row
|
67
33
|
end
|
34
|
+
sheet_index += 1
|
68
35
|
end
|
69
36
|
end
|
70
37
|
end
|
data/lib/workbook/row.rb
CHANGED
@@ -20,7 +20,7 @@ module Workbook
|
|
20
20
|
cells.each do |c|
|
21
21
|
c = c.clone if options[:clone_cells]
|
22
22
|
unless c.is_a? Workbook::Cell
|
23
|
-
c = Workbook::Cell.new(c)
|
23
|
+
c = Workbook::Cell.new(c, {row:self})
|
24
24
|
c.parse!(options[:cell_parse_options]) if options[:parse_cells_on_batch_creation]
|
25
25
|
end
|
26
26
|
push c
|
@@ -62,14 +62,14 @@ module Workbook
|
|
62
62
|
# Add cell
|
63
63
|
# @param [Workbook::Cell, Numeric,String,Time,Date,TrueClass,FalseClass,NilClass] cell or value to add
|
64
64
|
def push(cell)
|
65
|
-
cell = Workbook::Cell.new(cell) unless cell.class == Workbook::Cell
|
65
|
+
cell = Workbook::Cell.new(cell, {row:self}) unless cell.class == Workbook::Cell
|
66
66
|
super(cell)
|
67
67
|
end
|
68
68
|
|
69
69
|
# Add cell
|
70
70
|
# @param [Workbook::Cell, Numeric,String,Time,Date,TrueClass,FalseClass,NilClass] cell or value to add
|
71
71
|
def <<(cell)
|
72
|
-
cell = Workbook::Cell.new(cell) unless cell.class == Workbook::Cell
|
72
|
+
cell = Workbook::Cell.new(cell, {row:self}) unless cell.class == Workbook::Cell
|
73
73
|
super(cell)
|
74
74
|
end
|
75
75
|
|
@@ -108,7 +108,7 @@ module Workbook
|
|
108
108
|
end
|
109
109
|
return rv
|
110
110
|
elsif index_or_hash.is_a? String
|
111
|
-
symbolized = Workbook::Cell.new(index_or_hash).to_sym
|
111
|
+
symbolized = Workbook::Cell.new(index_or_hash, {row:self}).to_sym
|
112
112
|
self[symbolized]
|
113
113
|
else
|
114
114
|
if index_or_hash
|
@@ -131,7 +131,7 @@ module Workbook
|
|
131
131
|
if index_or_hash.is_a? Symbol
|
132
132
|
index = table_header_keys.index(index_or_hash)
|
133
133
|
elsif index_or_hash.is_a? String
|
134
|
-
symbolized = Workbook::Cell.new(index_or_hash).to_sym
|
134
|
+
symbolized = Workbook::Cell.new(index_or_hash, {row:self}).to_sym
|
135
135
|
index = table_header_keys.index(symbolized)
|
136
136
|
end
|
137
137
|
|
@@ -145,6 +145,7 @@ module Workbook
|
|
145
145
|
end
|
146
146
|
value_celled.value=(value)
|
147
147
|
end
|
148
|
+
value_celled.row = self
|
148
149
|
super(index,value_celled)
|
149
150
|
end
|
150
151
|
|
@@ -214,6 +215,13 @@ module Workbook
|
|
214
215
|
return hash
|
215
216
|
end
|
216
217
|
|
218
|
+
# Quick assessor to the book's template, if it exists
|
219
|
+
#
|
220
|
+
# @return [Workbook::Template]
|
221
|
+
def template
|
222
|
+
table.template if table
|
223
|
+
end
|
224
|
+
|
217
225
|
# Returns a hash representation of this row
|
218
226
|
#
|
219
227
|
# it differs from #to_hash as it doesn't contain the Workbook's Workbook::Cell-objects,
|
data/lib/workbook/sheet.rb
CHANGED
data/lib/workbook/table.rb
CHANGED
@@ -231,5 +231,19 @@ module Workbook
|
|
231
231
|
self
|
232
232
|
end
|
233
233
|
|
234
|
+
# Returns The dimensions of this sheet based on longest row
|
235
|
+
# @return [Array] x-width, y-height
|
236
|
+
def dimensions
|
237
|
+
height = self.count
|
238
|
+
width = self.collect{|a| a.length}.max
|
239
|
+
[width,height]
|
240
|
+
end
|
241
|
+
|
242
|
+
def columns
|
243
|
+
@columns ||= header.collect do |header_cell|
|
244
|
+
Column.new(self)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
234
248
|
end
|
235
249
|
end
|
data/lib/workbook/template.rb
CHANGED
data/lib/workbook/types/Date.rb
CHANGED
@@ -1,9 +1,21 @@
|
|
1
|
-
require 'workbook/cell'
|
1
|
+
# require 'workbook/modules/cell'
|
2
2
|
|
3
3
|
module Workbook
|
4
4
|
module Types
|
5
5
|
class Date < Date
|
6
|
-
include Workbook::Cell
|
6
|
+
include Workbook::Modules::Cell
|
7
|
+
|
8
|
+
def initialize(*args)
|
9
|
+
super(*args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def value
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def value= a
|
17
|
+
throw "#value= is no longer available"
|
18
|
+
end
|
7
19
|
end
|
8
20
|
end
|
9
21
|
end
|
data/lib/workbook/version.rb
CHANGED
@@ -1,82 +1,112 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
|
-
require '
|
2
|
+
require 'axlsx'
|
3
3
|
require 'workbook/readers/xls_shared'
|
4
4
|
|
5
5
|
module Workbook
|
6
6
|
module Writers
|
7
7
|
module XlsxWriter
|
8
8
|
|
9
|
-
# Generates an
|
9
|
+
# Generates an axlsx doc, ready for export to XLSX
|
10
10
|
#
|
11
11
|
# @param [Hash] options A hash with options (unused so far)
|
12
|
-
# @return [
|
12
|
+
# @return [Axlsx::Package] An object, ready for writing or more lower level operations
|
13
13
|
def to_xlsx options={}
|
14
|
-
|
15
|
-
book
|
14
|
+
formats_to_xlsx_format
|
15
|
+
book = init_xlsx_spreadsheet_template.workbook
|
16
16
|
book.worksheets.pop(book.worksheets.count - self.count) if book.worksheets and book.worksheets.count > self.count
|
17
17
|
self.each_with_index do |s,si|
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
xlsx_sheet = xlsx_sheet(si)
|
19
|
+
xlsx_sheet.name = s.name
|
21
20
|
s.table.each_with_index do |r, ri|
|
21
|
+
xlsx_row = xlsx_sheet[ri] ? xlsx_sheet[ri] : xlsx_sheet.add_row
|
22
|
+
xlsx_row.height = 16
|
23
|
+
xlsx_row_a = xlsx_row.to_ary
|
22
24
|
r.each_with_index do |c, ci|
|
23
|
-
|
24
|
-
|
25
|
+
xlsx_row.add_cell(c.value) unless xlsx_row_a[ci]
|
26
|
+
xlsx_cell = xlsx_row_a[ci]
|
27
|
+
xlsx_cell.value = c.value
|
28
|
+
xlsx_cell.style = c.format.raws[Fixnum] if c.format.raws[Fixnum]
|
25
29
|
end
|
30
|
+
xlsx_sheet.send(:update_column_info, xlsx_row.cells, [])
|
26
31
|
end
|
27
|
-
(
|
28
|
-
|
29
|
-
|
30
|
-
xls_sheet.delete_row(row_to_remove)
|
31
|
-
# xls_sheet.row_updated(row_to_remove, xls_sheet.row(row_to_remove))
|
32
|
+
(xlsx_sheet.rows.count - s.table.count).times do |time|
|
33
|
+
xlsx_sheet.rows.pop
|
32
34
|
end
|
33
|
-
# xls_sheet.updated_from(s.table.count)
|
34
|
-
# xls_sheet.dimensions
|
35
|
-
|
36
35
|
end
|
37
|
-
|
36
|
+
init_xlsx_spreadsheet_template
|
38
37
|
end
|
39
38
|
|
40
|
-
# Generates an
|
39
|
+
# Generates an string ready to be streamed as XLSX
|
41
40
|
#
|
42
41
|
# @param [Hash] options A hash with options (unused so far)
|
43
42
|
# @return [String] A string, ready for streaming, e.g. `send_data workbook.stream_xlsx`
|
44
43
|
def stream_xlsx options = {}
|
45
|
-
to_xlsx(options).
|
44
|
+
to_xlsx(options).to_stream.string
|
46
45
|
end
|
47
46
|
|
48
|
-
# Write the current workbook to Microsoft Excel's
|
47
|
+
# Write the current workbook to Microsoft Excel's XML format (using the Axlsx gem)
|
49
48
|
#
|
50
49
|
# @param [String] filename
|
51
50
|
# @param [Hash] options see #to_xlsx
|
52
51
|
def write_to_xlsx filename="#{title}.xlsx", options={}
|
53
|
-
if to_xlsx(options).
|
52
|
+
if to_xlsx(options).serialize(filename)
|
54
53
|
return filename
|
55
54
|
end
|
56
55
|
end
|
57
56
|
|
58
57
|
def xlsx_sheet a
|
59
|
-
if xlsx_template.worksheets[a]
|
60
|
-
return xlsx_template.worksheets[a]
|
58
|
+
if xlsx_template.workbook.worksheets[a]
|
59
|
+
return xlsx_template.workbook.worksheets[a]
|
61
60
|
else
|
62
|
-
xlsx_template.
|
63
|
-
self.
|
61
|
+
xlsx_template.workbook.add_worksheet
|
62
|
+
self.xlsx_sheet a
|
64
63
|
end
|
65
64
|
end
|
66
65
|
|
67
66
|
def xlsx_template
|
68
|
-
return template.raws[
|
67
|
+
return template.raws[Axlsx::Package]
|
69
68
|
end
|
70
69
|
|
71
70
|
def init_xlsx_spreadsheet_template
|
72
|
-
if self.xlsx_template.is_a?
|
71
|
+
if self.xlsx_template.is_a? Axlsx::Package
|
73
72
|
return self.xlsx_template
|
74
73
|
else
|
75
|
-
t =
|
74
|
+
t = Axlsx::Package.new
|
76
75
|
template.add_raw t
|
76
|
+
template.set_default_formats!
|
77
77
|
return t
|
78
78
|
end
|
79
79
|
end
|
80
|
+
|
81
|
+
def formats_to_xlsx_format
|
82
|
+
template.formats.each do |n,v|
|
83
|
+
v.each do | t,s |
|
84
|
+
format_to_xlsx_format(s)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def format_to_xlsx_format f
|
90
|
+
xlsfmt = nil
|
91
|
+
unless f.is_a? Workbook::Format
|
92
|
+
f = Workbook::Format.new f
|
93
|
+
end
|
94
|
+
|
95
|
+
xlsfmt={}
|
96
|
+
xlsfmt[:fg_color] = "#{f[:color]}00" if f[:color]
|
97
|
+
xlsfmt[:b] = true if f[:font_weight] == "bold" or f[:font_weight].to_i >= 600 or f[:"font_style"].to_s.match "oblique"
|
98
|
+
xlsfmt[:i] = true if f[:font_style] == "italic"
|
99
|
+
xlsfmt[:u] = true if f[:text_decoration].to_s.match("underline")
|
100
|
+
xlsfmt[:bg_color] = f[:background_color] if f[:background_color]
|
101
|
+
xlsfmt[:format_code] = strftime_to_ms_format(f[:number_format]) if f[:number_format]
|
102
|
+
xlsfmt[:font_name] = f[:font_family].split.first if f[:font_family]
|
103
|
+
xlsfmt[:family] = parse_font_family(f) if f[:font_family]
|
104
|
+
f.add_raw init_xlsx_spreadsheet_template.workbook.styles.add_style(xlsfmt)
|
105
|
+
# wb.styles{|a| p a.add_style({}).class }
|
106
|
+
|
107
|
+
f.add_raw xlsfmt
|
108
|
+
return xlsfmt
|
109
|
+
end
|
80
110
|
end
|
81
111
|
end
|
82
112
|
end
|
data/test/test_column.rb
CHANGED
@@ -3,6 +3,16 @@ require File.join(File.dirname(__FILE__), 'helper')
|
|
3
3
|
|
4
4
|
class TestColumn < Minitest::Test
|
5
5
|
|
6
|
+
def new_table
|
7
|
+
Workbook::Table.new([
|
8
|
+
["a","b","c","d"],
|
9
|
+
[true,3.2,"asdf",1],
|
10
|
+
[true,3.2,"asdf",1],
|
11
|
+
[false,3.2,"asdf",1],
|
12
|
+
[true,3.2,"asdf",1]
|
13
|
+
])
|
14
|
+
end
|
15
|
+
|
6
16
|
def test_init
|
7
17
|
c = Workbook::Column.new
|
8
18
|
assert_equal(Workbook::Column, c.class)
|
@@ -21,4 +31,24 @@ class TestColumn < Minitest::Test
|
|
21
31
|
assert_equal(Workbook::Table.new, c.table)
|
22
32
|
assert_raises(ArgumentError) { c.table = false }
|
23
33
|
end
|
34
|
+
|
35
|
+
def test_index
|
36
|
+
t = new_table
|
37
|
+
assert_equal(t.columns.first.index, 0)
|
38
|
+
assert_equal(t.columns.last.index, 3)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_column_type
|
42
|
+
t = new_table
|
43
|
+
assert_equal([:boolean, :float, :string, :integer], t.columns.collect{|a| a.column_type})
|
44
|
+
t = new_table
|
45
|
+
t.last.last.value = 1.1
|
46
|
+
assert_equal([:boolean, :float, :string, :string], t.columns.collect{|a| a.column_type})
|
47
|
+
t = new_table
|
48
|
+
t[2][3] = nil
|
49
|
+
assert_equal([:boolean, :float, :string, :integer], t.columns.collect{|a| a.column_type})
|
50
|
+
t = new_table
|
51
|
+
t[2].delete_at(3)
|
52
|
+
assert_equal([:boolean, :float, :string, :integer], t.columns.collect{|a| a.column_type})
|
53
|
+
end
|
24
54
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
require File.join(File.dirname(__FILE__), 'helper')
|
3
3
|
|
4
|
-
class
|
4
|
+
class TestModulesCell < Minitest::Test
|
5
5
|
|
6
6
|
|
7
7
|
def test_init
|
@@ -115,4 +115,11 @@ class TestCell < Minitest::Test
|
|
115
115
|
assert_equal(3,c.rowspan)
|
116
116
|
assert_equal(nil,c.colspan)
|
117
117
|
end
|
118
|
+
|
119
|
+
def cell_type
|
120
|
+
{1 => :integer, 3.2 => :float, true => :boolean, "asdf" => :string}.each do |k,v|
|
121
|
+
c = Workbook::Cel.new(k)
|
122
|
+
assert_equal(v,c.cell_type)
|
123
|
+
end
|
124
|
+
end
|
118
125
|
end
|
@@ -9,7 +9,7 @@ module Readers
|
|
9
9
|
assert_equal(90588,w.sheet.table[2][:b].value)
|
10
10
|
assert_equal(DateTime.new(2011,11,15),w.sheet.table[3][:d].value)
|
11
11
|
# assert_equal("#CCFFCC",w.sheet.table[3][:c].format[:background_color]) #colour compatibility turned off for now...
|
12
|
-
#
|
12
|
+
#assert_equal(8,w.sheet.table.first[:b].format[:width].round)
|
13
13
|
# assert_equal(4,w.sheet.table.first[:a].format[:width].round)
|
14
14
|
# assert_equal(25,w.sheet.table.first[:c].format[:width].round)
|
15
15
|
end
|
@@ -29,11 +29,5 @@ module Readers
|
|
29
29
|
assert_equal(nil,w.ms_formatting_to_strftime(nil));
|
30
30
|
assert_equal(nil,w.ms_formatting_to_strftime(""));
|
31
31
|
end
|
32
|
-
# def test_monkey_patched_ruby_xl_is_date_format?
|
33
|
-
# w = RubyXL::Workbook.new
|
34
|
-
# assert_equal(false, w.is_date_format?(nil))
|
35
|
-
# assert_equal(false, w.is_date_format?(""))
|
36
|
-
# assert_equal(true, w.is_date_format?("D-M-YYY"))
|
37
|
-
# end
|
38
32
|
end
|
39
33
|
end
|