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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/README.md +14 -15
  4. data/lib/workbook.rb +22 -11
  5. data/lib/workbook/book.rb +47 -25
  6. data/lib/workbook/cell.rb +20 -26
  7. data/lib/workbook/generatetypes.rb +14 -0
  8. data/lib/workbook/modules/cache.rb +52 -0
  9. data/lib/workbook/modules/{table_diff_sort.rb → diff_sort.rb} +64 -16
  10. data/lib/workbook/modules/raw_objects_storage.rb +7 -2
  11. data/lib/workbook/readers/ods_reader.rb +1 -1
  12. data/lib/workbook/readers/xls_reader.rb +55 -55
  13. data/lib/workbook/readers/xls_shared.rb +47 -0
  14. data/lib/workbook/readers/xlsx_reader.rb +34 -153
  15. data/lib/workbook/row.rb +47 -4
  16. data/lib/workbook/sheet.rb +4 -0
  17. data/lib/workbook/table.rb +36 -16
  18. data/lib/workbook/types/Date.rb +9 -0
  19. data/lib/workbook/types/False.rb +0 -0
  20. data/lib/workbook/types/FalseClass.rb +9 -0
  21. data/lib/workbook/types/Nil.rb +0 -0
  22. data/lib/workbook/types/NilClass.rb +9 -0
  23. data/lib/workbook/types/Numeric.rb +9 -0
  24. data/lib/workbook/types/String.rb +9 -0
  25. data/lib/workbook/types/Time.rb +9 -0
  26. data/lib/workbook/types/True.rb +0 -0
  27. data/lib/workbook/types/TrueClass.rb +9 -0
  28. data/lib/workbook/version.rb +1 -1
  29. data/lib/workbook/writers/html_writer.rb +40 -18
  30. data/lib/workbook/writers/xls_writer.rb +47 -5
  31. data/lib/workbook/writers/xlsx_writer.rb +123 -0
  32. data/test/artifacts/bigtable.xls +0 -0
  33. data/test/artifacts/bigtable.xlsx +0 -0
  34. data/test/artifacts/simple_sheet.xlsx +0 -0
  35. data/test/artifacts/simple_sheet_many_sheets.xls +0 -0
  36. data/test/test_book.rb +50 -2
  37. data/test/test_cell.rb +1 -1
  38. data/test/test_format.rb +8 -0
  39. data/test/test_modules_cache.rb +68 -0
  40. data/test/test_modules_table_diff_sort.rb +12 -1
  41. data/test/test_readers_xls_reader.rb +6 -0
  42. data/test/test_readers_xlsx_reader.rb +10 -9
  43. data/test/test_row.rb +65 -8
  44. data/test/test_sheet.rb +8 -0
  45. data/test/test_table.rb +48 -0
  46. data/test/test_writers_html_writer.rb +18 -8
  47. data/test/test_writers_xls_writer.rb +90 -0
  48. data/test/test_writers_xlsx_writer.rb +153 -0
  49. data/workbook.gemspec +9 -7
  50. metadata +71 -31
@@ -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
@@ -1,5 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
- require 'workbook/modules/table_diff_sort'
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
- if index_or_string
162
- return to_a[index_or_string]
163
- end
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
 
@@ -0,0 +1,9 @@
1
+ require 'workbook/cell'
2
+
3
+ module Workbook
4
+ module Types
5
+ class Date < Date
6
+ include Workbook::Cell
7
+ end
8
+ end
9
+ end
File without changes
@@ -0,0 +1,9 @@
1
+ require 'workbook/cell'
2
+
3
+ module Workbook
4
+ module Types
5
+ class FalseClass < FalseClass
6
+ include Workbook::Cell
7
+ end
8
+ end
9
+ end
File without changes
@@ -0,0 +1,9 @@
1
+ require 'workbook/cell'
2
+
3
+ module Workbook
4
+ module Types
5
+ class NilClass < NilClass
6
+ include Workbook::Cell
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'workbook/cell'
2
+
3
+ module Workbook
4
+ module Types
5
+ class Numeric < Numeric
6
+ include Workbook::Cell
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'workbook/cell'
2
+
3
+ module Workbook
4
+ module Types
5
+ class String < String
6
+ include Workbook::Cell
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'workbook/cell'
2
+
3
+ module Workbook
4
+ module Types
5
+ class Time < Time
6
+ include Workbook::Cell
7
+ end
8
+ end
9
+ end
File without changes
@@ -0,0 +1,9 @@
1
+ require 'workbook/cell'
2
+
3
+ module Workbook
4
+ module Types
5
+ class TrueClass < TrueClass
6
+ include Workbook::Cell
7
+ end
8
+ end
9
+ end
@@ -1,4 +1,4 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  module Workbook
3
- VERSION = '0.4.6.0'
3
+ VERSION = '0.4.7'
4
4
  end
@@ -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
- self.each{|row|
56
- doc.tr {
57
- row.each {|cell|
58
- classnames = cell.format.all_names.join(" ").strip
59
- td_options = classnames != "" ? {:class=>classnames} : {}
60
- td_options = td_options.merge({:style=>cell.format.to_css}) if options[:style_with_inline_css] and cell.format.to_css != ""
61
- td_options = td_options.merge({:colspan=>cell.colspan}) if cell.colspan
62
- td_options = td_options.merge({:rowspan=>cell.rowspan}) if cell.rowspan
63
- unless cell.value.class == Workbook::NilValue
64
- doc.td(td_options) {
65
- doc.text cell.value
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 XlS
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 = book.worksheet si
16
- xls_sheet = book.create_worksheet if xls_sheet == nil
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[:font_family].split.last if f[:font_family]
53
- xlsfmt.font.color = html_color_to_xls_color(f[:color]) if f[:color]
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