workbook 0.4.6.0 → 0.4.7
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/.gitignore +1 -0
- data/README.md +14 -15
- data/lib/workbook.rb +22 -11
- data/lib/workbook/book.rb +47 -25
- data/lib/workbook/cell.rb +20 -26
- data/lib/workbook/generatetypes.rb +14 -0
- data/lib/workbook/modules/cache.rb +52 -0
- data/lib/workbook/modules/{table_diff_sort.rb → diff_sort.rb} +64 -16
- data/lib/workbook/modules/raw_objects_storage.rb +7 -2
- data/lib/workbook/readers/ods_reader.rb +1 -1
- data/lib/workbook/readers/xls_reader.rb +55 -55
- data/lib/workbook/readers/xls_shared.rb +47 -0
- data/lib/workbook/readers/xlsx_reader.rb +34 -153
- data/lib/workbook/row.rb +47 -4
- data/lib/workbook/sheet.rb +4 -0
- data/lib/workbook/table.rb +36 -16
- data/lib/workbook/types/Date.rb +9 -0
- data/lib/workbook/types/False.rb +0 -0
- data/lib/workbook/types/FalseClass.rb +9 -0
- data/lib/workbook/types/Nil.rb +0 -0
- data/lib/workbook/types/NilClass.rb +9 -0
- data/lib/workbook/types/Numeric.rb +9 -0
- data/lib/workbook/types/String.rb +9 -0
- data/lib/workbook/types/Time.rb +9 -0
- data/lib/workbook/types/True.rb +0 -0
- data/lib/workbook/types/TrueClass.rb +9 -0
- data/lib/workbook/version.rb +1 -1
- data/lib/workbook/writers/html_writer.rb +40 -18
- data/lib/workbook/writers/xls_writer.rb +47 -5
- data/lib/workbook/writers/xlsx_writer.rb +123 -0
- data/test/artifacts/bigtable.xls +0 -0
- data/test/artifacts/bigtable.xlsx +0 -0
- data/test/artifacts/simple_sheet.xlsx +0 -0
- data/test/artifacts/simple_sheet_many_sheets.xls +0 -0
- data/test/test_book.rb +50 -2
- data/test/test_cell.rb +1 -1
- data/test/test_format.rb +8 -0
- data/test/test_modules_cache.rb +68 -0
- data/test/test_modules_table_diff_sort.rb +12 -1
- data/test/test_readers_xls_reader.rb +6 -0
- data/test/test_readers_xlsx_reader.rb +10 -9
- data/test/test_row.rb +65 -8
- data/test/test_sheet.rb +8 -0
- data/test/test_table.rb +48 -0
- data/test/test_writers_html_writer.rb +18 -8
- data/test/test_writers_xls_writer.rb +90 -0
- data/test/test_writers_xlsx_writer.rb +153 -0
- data/workbook.gemspec +9 -7
- metadata +71 -31
data/lib/workbook/sheet.rb
CHANGED
@@ -34,6 +34,10 @@ module Workbook
|
|
34
34
|
first
|
35
35
|
end
|
36
36
|
|
37
|
+
def name
|
38
|
+
@name ||= "Sheet #{book.index(self)+1}"
|
39
|
+
end
|
40
|
+
|
37
41
|
# Set the first table of this sheet with a table or array of cells/values
|
38
42
|
# @param [Workbook::Table, Array<Array>] table The new first table of this sheet
|
39
43
|
# @param [Hash] options are forwarded to Workbook::Table.new
|
data/lib/workbook/table.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
|
-
require 'workbook/modules/
|
2
|
+
require 'workbook/modules/diff_sort'
|
3
3
|
require 'workbook/writers/csv_table_writer'
|
4
4
|
require 'workbook/writers/json_table_writer'
|
5
5
|
require 'workbook/writers/html_writer'
|
@@ -29,6 +29,13 @@ module Workbook
|
|
29
29
|
# Column data is considered as a 'row' with 'cells' that contain 'formatting'
|
30
30
|
end
|
31
31
|
|
32
|
+
# Quick assessor to the book's template, if it exists
|
33
|
+
#
|
34
|
+
# @return [Workbook::Template]
|
35
|
+
def template
|
36
|
+
sheet.book.template
|
37
|
+
end
|
38
|
+
|
32
39
|
# Returns the header of this table (typically the first row, but can be a different row).
|
33
40
|
# The header row is also used for finding values in a aribrary row.
|
34
41
|
#
|
@@ -86,18 +93,6 @@ module Workbook
|
|
86
93
|
row.set_table(self)
|
87
94
|
end
|
88
95
|
|
89
|
-
# Overrides normal Row's []=-function; automatically converting to row and setting
|
90
|
-
# with the label correctly
|
91
|
-
#
|
92
|
-
# @param [Fixnum] index
|
93
|
-
# @param [Workbook::Table, Array] row to set
|
94
|
-
# @return [Workbook::Cell, nil]
|
95
|
-
def []= (index, row)
|
96
|
-
row = Workbook::Row.new(row) if row.class == Array
|
97
|
-
super(index,row)
|
98
|
-
row.set_table(self)
|
99
|
-
end
|
100
|
-
|
101
96
|
def has_contents?
|
102
97
|
self.clone.remove_empty_lines!.count != 0
|
103
98
|
end
|
@@ -157,10 +152,35 @@ module Workbook
|
|
157
152
|
cell_index = alpha_index_to_number_index(match[1])
|
158
153
|
row_index = match[2].to_i - 1
|
159
154
|
return self[row_index][cell_index]
|
155
|
+
elsif index_or_string.is_a? Range
|
156
|
+
collection = to_a[index_or_string].collect{|a| a.clone}
|
157
|
+
return Workbook::Table.new collection
|
158
|
+
elsif index_or_string.is_a? Integer
|
159
|
+
return to_a[index_or_string]
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# Overrides normal Row's []=-function; automatically converting to row and setting
|
164
|
+
# with the label correctly
|
165
|
+
#
|
166
|
+
# @example Lookup using fixnum or header value encoded as symbol
|
167
|
+
# `table[0] = <Row [a,2,3,4]>` (set first row)
|
168
|
+
# `table["A1"] = 2` (set first cell of first row to 2)
|
169
|
+
#
|
170
|
+
# @param [Fixnum, String] index_or_string to reference to either the row, or the cell
|
171
|
+
# @param [Workbook::Table, Array] new_value to set
|
172
|
+
# @return [Workbook::Cell, nil]
|
173
|
+
def []= (index_or_string, new_value)
|
174
|
+
if index_or_string.is_a? String
|
175
|
+
match = index_or_string.upcase.match(/([A-Z]*)([0-9]*)/)
|
176
|
+
cell_index = alpha_index_to_number_index(match[1])
|
177
|
+
row_index = match[2].to_i - 1
|
178
|
+
self[row_index][cell_index].value = new_value
|
160
179
|
else
|
161
|
-
|
162
|
-
|
163
|
-
|
180
|
+
row = new_value
|
181
|
+
row = Workbook::Row.new(row) unless row.is_a? Workbook::Row
|
182
|
+
super(index_or_string,row)
|
183
|
+
row.set_table(self)
|
164
184
|
end
|
165
185
|
end
|
166
186
|
|
File without changes
|
File without changes
|
File without changes
|
data/lib/workbook/version.rb
CHANGED
@@ -47,28 +47,50 @@ module Workbook
|
|
47
47
|
# Generates an HTML table ()
|
48
48
|
#
|
49
49
|
# @param [Hash] options A hash with options
|
50
|
-
# @return [String] A String containing the HTML code
|
50
|
+
# @return [String] A String containing the HTML code, most importantly `:style_with_inline_css` (default false)
|
51
51
|
def to_html options={}
|
52
52
|
options = {:style_with_inline_css=>false}.merge(options)
|
53
53
|
builder = Nokogiri::XML::Builder.new do |doc|
|
54
|
-
doc.table
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
doc.
|
66
|
-
|
54
|
+
doc.table do
|
55
|
+
doc.thead do
|
56
|
+
if header
|
57
|
+
doc.tr do
|
58
|
+
header.each do |cell|
|
59
|
+
classnames = cell.format.all_names.join(" ").strip
|
60
|
+
td_options = classnames != "" ? {:class=>classnames} : {}
|
61
|
+
td_options = td_options.merge({:style=>cell.format.to_css}) if options[:style_with_inline_css] and cell.format.to_css != ""
|
62
|
+
td_options = td_options.merge({:colspan=>cell.colspan}) if cell.colspan
|
63
|
+
td_options = td_options.merge({:rowspan=>cell.rowspan}) if cell.rowspan
|
64
|
+
unless cell.value.class == Workbook::NilValue
|
65
|
+
doc.th(td_options) do
|
66
|
+
doc.text cell.value
|
67
|
+
end
|
68
|
+
end
|
67
69
|
end
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
doc.tbody do
|
74
|
+
self.each do |row|
|
75
|
+
unless row.header?
|
76
|
+
doc.tr do
|
77
|
+
row.each do |cell|
|
78
|
+
classnames = cell.format.all_names.join(" ").strip
|
79
|
+
td_options = classnames != "" ? {:class=>classnames} : {}
|
80
|
+
td_options = td_options.merge({:style=>cell.format.to_css}) if options[:style_with_inline_css] and cell.format.to_css != ""
|
81
|
+
td_options = td_options.merge({:colspan=>cell.colspan}) if cell.colspan
|
82
|
+
td_options = td_options.merge({:rowspan=>cell.rowspan}) if cell.rowspan
|
83
|
+
unless cell.value.class == Workbook::NilValue
|
84
|
+
doc.td(td_options) do
|
85
|
+
doc.text cell.value
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
72
94
|
end
|
73
95
|
return builder.doc.to_xhtml
|
74
96
|
end
|
@@ -5,15 +5,16 @@ module Workbook
|
|
5
5
|
module Writers
|
6
6
|
module XlsWriter
|
7
7
|
|
8
|
-
# Generates an Spreadsheet (from the spreadsheet gem) in order to build an
|
8
|
+
# Generates an Spreadsheet (from the spreadsheet gem) in order to build an xls
|
9
9
|
#
|
10
10
|
# @param [Hash] options A hash with options (unused so far)
|
11
11
|
# @return [Spreadsheet] A Spreadsheet object, ready for writing or more lower level operations
|
12
12
|
def to_xls options={}
|
13
13
|
book = init_spreadsheet_template
|
14
14
|
self.each_with_index do |s,si|
|
15
|
-
xls_sheet =
|
16
|
-
xls_sheet =
|
15
|
+
xls_sheet = xls_sheet(si)
|
16
|
+
xls_sheet.name = s.name
|
17
|
+
|
17
18
|
s.table.each_with_index do |r, ri|
|
18
19
|
xls_sheet.row(ri).height= r.format[:height] if r.format
|
19
20
|
r.each_with_index do |c, ci|
|
@@ -27,6 +28,23 @@ module Workbook
|
|
27
28
|
end
|
28
29
|
end
|
29
30
|
end
|
31
|
+
(xls_sheet.last_row_index + 1 - s.table.count).times do |time|
|
32
|
+
row_to_remove = s.table.count+time
|
33
|
+
xls_sheet.row(row_to_remove).each_with_index do |c, ci|
|
34
|
+
xls_sheet.row(row_to_remove)[ci]=nil
|
35
|
+
end
|
36
|
+
|
37
|
+
xls_sheet.delete_row(row_to_remove)
|
38
|
+
xls_sheet.row_updated(row_to_remove, xls_sheet.row(row_to_remove))
|
39
|
+
end
|
40
|
+
xls_sheet.updated_from(s.table.count)
|
41
|
+
xls_sheet.dimensions
|
42
|
+
|
43
|
+
end
|
44
|
+
# kind of a hack, delting by popping from xls worksheet results in Excel-errors (not LibreOffice)
|
45
|
+
# book.worksheets.pop(book.worksheets.count - self.count) if book.worksheets and book.worksheets.count > self.count
|
46
|
+
book.worksheets.each_with_index do |sheet, si|
|
47
|
+
sheet.visibility = self[si] ? :visible : :strong_hidden
|
30
48
|
end
|
31
49
|
book
|
32
50
|
end
|
@@ -49,13 +67,37 @@ module Workbook
|
|
49
67
|
xlsfmt.number_format = strftime_to_ms_format(f[:number_format]) if f[:number_format]
|
50
68
|
xlsfmt.text_direction = f[:text_direction] if f[:text_direction]
|
51
69
|
xlsfmt.font.name = f[:font_family].split.first if f[:font_family]
|
52
|
-
xlsfmt.font.family = f
|
53
|
-
|
70
|
+
xlsfmt.font.family = parse_font_family(f) if f[:font_family]
|
71
|
+
color = html_color_to_xls_color(f[:color])
|
72
|
+
xlsfmt.font.color = color if color
|
54
73
|
f.add_raw xlsfmt
|
55
74
|
end
|
56
75
|
return xlsfmt
|
57
76
|
end
|
58
77
|
|
78
|
+
# Parses right font-family name
|
79
|
+
#
|
80
|
+
# @param [Workbook::Format, hash] format to parse
|
81
|
+
def parse_font_family(format)
|
82
|
+
font = format[:font_family].to_s.split.last
|
83
|
+
valid_values = [:none,:roman,:swiss,:modern,:script,:decorative]
|
84
|
+
if valid_values.include?(font)
|
85
|
+
return font
|
86
|
+
elsif valid_values.include?(font.to_s.downcase.to_sym)
|
87
|
+
return font.to_s.downcase.to_sym
|
88
|
+
else
|
89
|
+
font = font.to_s.downcase.strip
|
90
|
+
translation = {
|
91
|
+
"arial"=>:swiss,
|
92
|
+
"times"=>:roman,
|
93
|
+
"times new roman"=>:roman
|
94
|
+
}
|
95
|
+
tfont = translation[font]
|
96
|
+
return tfont ? tfont : :none
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
|
59
101
|
# Attempt to convert html-hex color value to xls color number
|
60
102
|
#
|
61
103
|
# @param [String] hex color
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'rubyXL'
|
3
|
+
require 'workbook/readers/xls_shared'
|
4
|
+
|
5
|
+
module Workbook
|
6
|
+
module Writers
|
7
|
+
module XlsxWriter
|
8
|
+
|
9
|
+
# Generates an Spreadsheet (from the spreadsheet gem) in order to build an XlS
|
10
|
+
#
|
11
|
+
# @param [Hash] options A hash with options (unused so far)
|
12
|
+
# @return [Spreadsheet] A Spreadsheet object, ready for writing or more lower level operations
|
13
|
+
def to_xlsx options={}
|
14
|
+
book = init_xlsx_spreadsheet_template
|
15
|
+
book.theme = RubyXL::Theme.new unless book.theme #workaround bug in rubyxl
|
16
|
+
book.worksheets.pop(book.worksheets.count - self.count) if book.worksheets and book.worksheets.count > self.count
|
17
|
+
self.each_with_index do |s,si|
|
18
|
+
xls_sheet = xlsx_sheet(si)
|
19
|
+
xls_sheet.sheet_name = s.name
|
20
|
+
|
21
|
+
s.table.each_with_index do |r, ri|
|
22
|
+
r.each_with_index do |c, ci|
|
23
|
+
xls_sheet.add_cell(ri, ci, c.value)
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
(xls_sheet.count + 1 - s.table.count).times do |time|
|
28
|
+
row_to_remove = s.table.count+time
|
29
|
+
|
30
|
+
xls_sheet.delete_row(row_to_remove)
|
31
|
+
# xls_sheet.row_updated(row_to_remove, xls_sheet.row(row_to_remove))
|
32
|
+
end
|
33
|
+
# xls_sheet.updated_from(s.table.count)
|
34
|
+
# xls_sheet.dimensions
|
35
|
+
|
36
|
+
end
|
37
|
+
book
|
38
|
+
end
|
39
|
+
|
40
|
+
# # Generates an Spreadsheet (from the spreadsheet gem) in order to build an XlS
|
41
|
+
# #
|
42
|
+
# # @param [Workbook::Format, Hash] f A Workbook::Format or hash with format-options (:font_weight, :rotation, :background_color, :number_format, :text_direction, :color, :font_family)
|
43
|
+
# # @return [Spreadsheet::Format] A Spreadsheet format-object, ready for writing or more lower level operations
|
44
|
+
# def format_to_xlsx_format f
|
45
|
+
# xlsfmt = nil
|
46
|
+
# unless f.is_a? Workbook::Format
|
47
|
+
# f = Workbook::Format.new f
|
48
|
+
# end
|
49
|
+
# xlsfmt = f.return_raw_for Spreadsheet::Format
|
50
|
+
# unless xlsfmt
|
51
|
+
# xlsfmt=Spreadsheet::Format.new :weight=>f[:font_weight]
|
52
|
+
# xlsfmt.rotation = f[:rotation] if f[:rotation]
|
53
|
+
# xlsfmt.pattern_fg_color = html_color_to_xls_color(f[:background_color]) if html_color_to_xls_color(f[:background_color])
|
54
|
+
# xlsfmt.pattern = 1 if html_color_to_xls_color(f[:background_color])
|
55
|
+
# xlsfmt.number_format = strftime_to_ms_format(f[:number_format]) if f[:number_format]
|
56
|
+
# xlsfmt.text_direction = f[:text_direction] if f[:text_direction]
|
57
|
+
# xlsfmt.font.name = f[:font_family].split.first if f[:font_family]
|
58
|
+
# xlsfmt.font.family = parse_font_family(f) if f[:font_family]
|
59
|
+
# color = html_color_to_xls_color(f[:color])
|
60
|
+
# xlsfmt.font.color = color if color
|
61
|
+
# f.add_raw xlsfmt
|
62
|
+
# end
|
63
|
+
# return xlsfmt
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# # Parses right font-family name
|
67
|
+
# #
|
68
|
+
# # @param [Workbook::Format, hash] format to parse
|
69
|
+
# def parse_font_family(format)
|
70
|
+
# font = format[:font_family].to_s.split.last
|
71
|
+
# valid_values = [:none,:roman,:swiss,:modern,:script,:decorative]
|
72
|
+
# if valid_values.include?(font)
|
73
|
+
# return font
|
74
|
+
# elsif valid_values.include?(font.to_s.downcase.to_sym)
|
75
|
+
# return font.to_s.downcase.to_sym
|
76
|
+
# else
|
77
|
+
# font = font.to_s.downcase.strip
|
78
|
+
# translation = {
|
79
|
+
# "arial"=>:swiss,
|
80
|
+
# "times"=>:roman,
|
81
|
+
# "times new roman"=>:roman
|
82
|
+
# }
|
83
|
+
# tfont = translation[font]
|
84
|
+
# return tfont ? tfont : :none
|
85
|
+
# end
|
86
|
+
# end
|
87
|
+
#
|
88
|
+
|
89
|
+
# Write the current workbook to Microsoft Excel format (using the spreadsheet gem)
|
90
|
+
#
|
91
|
+
# @param [String] filename
|
92
|
+
# @param [Hash] options see #to_xls
|
93
|
+
def write_to_xlsx filename="#{title}.xlsx", options={}
|
94
|
+
if to_xlsx(options).write(filename)
|
95
|
+
return filename
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def xlsx_sheet a
|
100
|
+
if xlsx_template.worksheets[a]
|
101
|
+
return xlsx_template.worksheets[a]
|
102
|
+
else
|
103
|
+
xlsx_template.create_worksheet
|
104
|
+
self.xls_sheet a
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def xlsx_template
|
109
|
+
return template.raws[RubyXL::Workbook]
|
110
|
+
end
|
111
|
+
|
112
|
+
def init_xlsx_spreadsheet_template
|
113
|
+
if self.xlsx_template.is_a? RubyXL::Workbook
|
114
|
+
return self.xlsx_template
|
115
|
+
else
|
116
|
+
t = RubyXL::Workbook.new
|
117
|
+
template.add_raw t
|
118
|
+
return t
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|