workbook 0.4.2 → 0.4.3

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 (80) hide show
  1. data/.gitignore +2 -1
  2. data/.yardoc/checksums +22 -21
  3. data/.yardoc/object_types +0 -0
  4. data/.yardoc/objects/root.dat +0 -0
  5. data/Gemfile.lock +2 -2
  6. data/doc/RubyXL.html +1 -1
  7. data/doc/RubyXL/Cell.html +1 -1
  8. data/doc/RubyXL/Workbook.html +31 -31
  9. data/doc/Workbook.html +4 -4
  10. data/doc/Workbook/Book.html +5 -5
  11. data/doc/Workbook/Cell.html +202 -62
  12. data/doc/Workbook/Format.html +32 -32
  13. data/doc/Workbook/Modules.html +1 -1
  14. data/doc/Workbook/Modules/RawObjectsStorage.html +5 -5
  15. data/doc/Workbook/Modules/TableDiffSort.html +14 -14
  16. data/doc/Workbook/Modules/TypeParser.html +7 -7
  17. data/doc/Workbook/NilValue.html +434 -0
  18. data/doc/Workbook/Readers.html +3 -3
  19. data/doc/Workbook/Readers/CsvReader.html +6 -6
  20. data/doc/Workbook/Readers/OdsReader.html +562 -60
  21. data/doc/Workbook/Readers/TxtReader.html +2 -2
  22. data/doc/Workbook/Readers/XlsReader.html +14 -14
  23. data/doc/Workbook/Readers/XlsShared.html +67 -67
  24. data/doc/Workbook/Readers/XlsxReader.html +7 -7
  25. data/doc/Workbook/Row.html +243 -5
  26. data/doc/Workbook/Sheet.html +2 -2
  27. data/doc/Workbook/Table.html +669 -58
  28. data/doc/Workbook/Template.html +5 -5
  29. data/doc/Workbook/Writers.html +1 -1
  30. data/doc/Workbook/Writers/CsvTableWriter.html +1 -1
  31. data/doc/Workbook/Writers/HtmlWriter.html +27 -13
  32. data/doc/Workbook/Writers/XlsWriter.html +2 -2
  33. data/doc/_index.html +23 -8
  34. data/doc/class_list.html +1 -1
  35. data/doc/file.README.html +16 -12
  36. data/doc/index.html +16 -12
  37. data/doc/method_list.html +232 -72
  38. data/doc/top-level-namespace.html +1 -1
  39. data/lib/workbook/book.rb +25 -25
  40. data/lib/workbook/cell.rb +33 -33
  41. data/lib/workbook/format.rb +10 -10
  42. data/lib/workbook/modules/raw_objects_storage.rb +19 -19
  43. data/lib/workbook/modules/table_diff_sort.rb +22 -22
  44. data/lib/workbook/modules/type_parser.rb +18 -18
  45. data/lib/workbook/nil_value.rb +6 -6
  46. data/lib/workbook/readers/csv_reader.rb +8 -8
  47. data/lib/workbook/readers/ods_reader.rb +92 -46
  48. data/lib/workbook/readers/txt_reader.rb +2 -2
  49. data/lib/workbook/readers/xls_reader.rb +19 -19
  50. data/lib/workbook/readers/xls_shared.rb +70 -70
  51. data/lib/workbook/readers/xlsx_reader.rb +42 -42
  52. data/lib/workbook/row.rb +59 -29
  53. data/lib/workbook/sheet.rb +8 -8
  54. data/lib/workbook/table.rb +52 -19
  55. data/lib/workbook/template.rb +10 -10
  56. data/lib/workbook/version.rb +1 -1
  57. data/lib/workbook/writers/csv_table_writer.rb +1 -1
  58. data/lib/workbook/writers/html_writer.rb +6 -6
  59. data/lib/workbook/writers/xls_writer.rb +12 -12
  60. data/rbeautify.rb +232 -0
  61. data/test/artifacts/book_with_colspans.ods +0 -0
  62. data/test/artifacts/book_with_tabs_and_colours.ods +0 -0
  63. data/test/test_book.rb +10 -10
  64. data/test/test_cell.rb +14 -14
  65. data/test/test_format.rb +11 -11
  66. data/test/test_functional.rb +3 -3
  67. data/test/test_modules_table_diff_sort.rb +24 -24
  68. data/test/test_modules_type_parser.rb +27 -27
  69. data/test/test_readers_csv_reader.rb +11 -11
  70. data/test/test_readers_ods_reader.rb +22 -15
  71. data/test/test_readers_txt_reader.rb +13 -13
  72. data/test/test_readers_xls_reader.rb +11 -11
  73. data/test/test_readers_xlsx_reader.rb +5 -5
  74. data/test/test_row.rb +96 -26
  75. data/test/test_sheet.rb +9 -9
  76. data/test/test_table.rb +42 -26
  77. data/test/test_template.rb +3 -3
  78. data/test/test_writers_html_writer.rb +6 -4
  79. data/test/test_writers_xls_writer.rb +7 -7
  80. metadata +5 -1
@@ -1,13 +1,13 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  module Workbook
3
- module Modules
3
+ module Modules
4
4
  # Adds diffing and sorting functions
5
- module TableDiffSort
5
+ module TableDiffSort
6
6
  # create an overview of the differences between itself with another 'previous' table, returns a book with a single sheet and table (containing the diffs)
7
7
  #
8
8
  # @return [Workbook::Book] (note should and will become Workbook::Table as diffing occurs on table level...)
9
9
  def diff other, options={:sort=>true,:ignore_headers=>false}
10
-
10
+
11
11
  aligned = align(other, options)
12
12
  aself = aligned[:self]
13
13
  aother = aligned[:other]
@@ -24,7 +24,7 @@ module Workbook
24
24
  row = diff_table[ri] = Workbook::Row.new(nil, diff_table)
25
25
  srow = aself[ri]
26
26
  orow = aother[ri]
27
-
27
+
28
28
  iteration_cols.each_with_index do |ch, ci|
29
29
  scell = srow[ch]
30
30
  ocell = orow[ch]
@@ -45,7 +45,7 @@ module Workbook
45
45
  f = diff_template.template.create_or_find_format_by 'updated'
46
46
  dcell.format = f
47
47
  end
48
-
48
+
49
49
  row[ci]=dcell
50
50
  end
51
51
  end
@@ -55,7 +55,7 @@ module Workbook
55
55
 
56
56
  diff_table
57
57
  end
58
-
58
+
59
59
  def diff_template
60
60
  return @diff_template if @diff_template
61
61
  diffbook = Workbook::Book.new
@@ -74,12 +74,12 @@ module Workbook
74
74
  @diff_template = diffbook
75
75
  return difftable
76
76
  end
77
-
77
+
78
78
  # aligns itself with another table, used by diff
79
79
  def align other, options={:sort=>true,:ignore_headers=>false}
80
-
80
+
81
81
  options = {:sort=>true,:ignore_headers=>false}.merge(options)
82
-
82
+
83
83
  iteration_cols = nil
84
84
  sother = other.clone.remove_empty_lines!
85
85
  sself = self.clone.remove_empty_lines!
@@ -88,20 +88,20 @@ module Workbook
88
88
  sother.header = false
89
89
  sself.header = false
90
90
  end
91
-
91
+
92
92
  sother = options[:sort] ? Workbook::Table.new(sother.sort) : sother
93
93
  sself = options[:sort] ? Workbook::Table.new(sself.sort) : sself
94
-
94
+
95
95
  iteration_rows = [sother.count,sself.count].max.times.collect
96
96
 
97
97
  row_index = 0
98
98
  while row_index < [sother.count,sself.count].max and row_index < other.count+self.count do
99
99
  row_index = align_row(sself, sother, row_index)
100
100
  end
101
-
102
- {:self=>sself, :other=>sother}
101
+
102
+ {:self=>sself, :other=>sother}
103
103
  end
104
-
104
+
105
105
  # for use in the align 'while' loop
106
106
  def align_row sself, sother, row_index
107
107
  asd = 0
@@ -119,25 +119,25 @@ module Workbook
119
119
  sself.insert row_index, placeholder_row
120
120
  row_index -=1
121
121
  end
122
-
122
+
123
123
  row_index += 1
124
124
  end
125
-
125
+
126
126
  def insert_placeholder? sother, sself, row_index
127
127
  (sother[row_index].nil? or !sother[row_index].placeholder?) and
128
128
  (sself[row_index].nil? or !sself[row_index].placeholder?)
129
129
  end
130
-
130
+
131
131
  # returns a placeholder row, for internal use only
132
- def placeholder_row
132
+ def placeholder_row
133
133
  if @placeholder_row != nil
134
- return @placeholder_row
134
+ return @placeholder_row
135
135
  else
136
136
  @placeholder_row = Workbook::Row.new [nil]
137
137
  placeholder_row.placeholder = true
138
- return @placeholder_row
138
+ return @placeholder_row
139
139
  end
140
140
  end
141
- end
142
- end
141
+ end
142
+ end
143
143
  end
@@ -3,19 +3,19 @@ module Workbook
3
3
  module Modules
4
4
  # Adds type parsing capabilities to e.g. a Cell.
5
5
  module TypeParser
6
-
6
+
7
7
  # Cleans a text file from all kinds of different ways of representing new lines
8
8
  # @param [String] csv_raw a raw csv string
9
9
  def strip_win_chars csv_raw
10
10
  csv_raw.gsub(/(\n\r|\r\n|\r)/,"\n")
11
11
  end
12
-
12
+
13
13
  # Return the different active string parsers
14
14
  # @return [Array<Symbol>] A list of parsers
15
15
  def string_parsers
16
16
  @string_parsers ||= [:string_cleaner,:string_nil_converter,:string_integer_converter,:string_boolean_converter]
17
17
  end
18
-
18
+
19
19
  # Set the list of string parsers
20
20
  # @param [Array<Symbol>] parsers A list of parsers
21
21
  # @return [Array<Symbol>] A list of parsers
@@ -24,11 +24,11 @@ module Workbook
24
24
  end
25
25
 
26
26
  # Return the different active string parsers
27
- # @return [Array<Proc>] A list of parsers as Procs
27
+ # @return [Array<Proc>] A list of parsers as Procs
28
28
  def string_parsers_as_procs
29
29
  string_parsers.collect{|c| c.is_a?(Proc) ? c : self.send(c)}
30
30
  end
31
-
31
+
32
32
  # Returns the parsed value (retrieved by calling #value)
33
33
  # @return [Object] The parsed object, ideally a date or integer when found to be a such...
34
34
  def parse options={}
@@ -40,30 +40,30 @@ module Workbook
40
40
  v = p.call(v)
41
41
  end
42
42
  end
43
- v
43
+ v
44
44
  end
45
-
45
+
46
46
  def parse! options={}
47
47
  self.value = parse(options)
48
48
  end
49
-
49
+
50
50
  def clean! options={}
51
51
  parse! options
52
52
  end
53
-
53
+
54
54
  def string_cleaner
55
55
  proc do |v|
56
- v = v.strip
56
+ v = v.strip
57
57
  v.gsub('mailto:','')
58
58
  end
59
59
  end
60
-
60
+
61
61
  def string_nil_converter
62
62
  proc do |v|
63
63
  (v == "" ? nil : v)
64
64
  end
65
65
  end
66
-
66
+
67
67
  def string_integer_converter
68
68
  proc do |v|
69
69
  if v.to_i.to_s == v
@@ -73,19 +73,19 @@ module Workbook
73
73
  end
74
74
  end
75
75
  end
76
-
76
+
77
77
  def string_optimistic_date_converter
78
- proc do |v|
78
+ proc do |v|
79
79
  rv = v
80
80
  starts_with_nr = v.chars.first.to_i.to_s == v.chars.first #it should at least start with a number...
81
81
  no_spaced_dash = v.to_s.match(" - ") ? false : true
82
82
  min_two_dashes = v.to_s.scan("-").count > 1 ? true : false
83
83
  min_two_dashes = v.to_s.scan("/").count > 1 ? true : false if min_two_dashes == false
84
-
84
+
85
85
  normal_date_length = v.to_s.length <= 25
86
86
  if no_spaced_dash and starts_with_nr and normal_date_length and min_two_dashes
87
87
  begin
88
- rv = (v.length > 10) ? DateTime.parse(v) : Date.parse(v)
88
+ rv = (v.length > 10) ? DateTime.parse(v) : Date.parse(v)
89
89
  rescue ArgumentError
90
90
  rv = v
91
91
  end
@@ -100,13 +100,13 @@ module Workbook
100
100
  rescue ArgumentError
101
101
  end
102
102
  end
103
- end
103
+ end
104
104
  rv
105
105
  end
106
106
  end
107
107
 
108
108
  # converts 'true' or 'false' strings in `true` or `false` values
109
- # return [Proc] that returns a boolean value if it is considered as such
109
+ # return [Proc] that returns a boolean value if it is considered as such
110
110
  def string_boolean_converter
111
111
  proc do |v|
112
112
  dv = v.downcase
@@ -1,25 +1,25 @@
1
1
  module Workbook
2
-
2
+
3
3
  # Used in cases col or rowspans are used
4
4
  class NilValue
5
5
  attr_accessor :reason #:covered
6
-
6
+
7
7
  # initialize this special nilvalue with a reason
8
8
  # @params [String] reason (currently only :covered, in case this cell is coverd because an adjecant cell spans over it)
9
9
  def initialize reason
10
10
  self.reason= reason
11
11
  end
12
-
12
+
13
13
  # returns the value of itself (nil)
14
14
  # @return [NilClass] nil
15
15
  def value
16
16
  nil
17
17
  end
18
-
18
+
19
19
  def <=> v
20
20
  value <=> v
21
21
  end
22
-
22
+
23
23
  # set the reason why this value is nil
24
24
  def reason= reason
25
25
  if reason == :covered
@@ -28,6 +28,6 @@ module Workbook
28
28
  raise "invalid reason given"
29
29
  end
30
30
  end
31
-
31
+
32
32
  end
33
33
  end
@@ -7,7 +7,7 @@ module Workbook
7
7
  csv = text
8
8
  parse_csv csv
9
9
  end
10
-
10
+
11
11
  def csv_lib
12
12
  if RUBY_VERSION < '1.9'
13
13
  require 'faster_csv'
@@ -16,7 +16,7 @@ module Workbook
16
16
  return CSV
17
17
  end
18
18
  end
19
-
19
+
20
20
  def parse_csv csv_raw
21
21
  custom_date_converter = Workbook::Cell.new.string_optimistic_date_converter
22
22
  converters = [:float,:integer,:date,:date_time,custom_date_converter]
@@ -24,18 +24,18 @@ module Workbook
24
24
  #begin
25
25
  csv = csv_lib.parse(csv_raw,{:converters=>converters})
26
26
 
27
- #rescue
28
- # we're going to have another shot at it...
29
- #end
30
-
31
- if csv==nil or csv[0].count == 1
27
+ #rescue
28
+ # we're going to have another shot at it...
29
+ #end
30
+
31
+ if csv==nil or csv[0].count == 1
32
32
  csv_excel = csv_lib.parse(csv_raw,{:converters=>converters,:col_sep=>';'})
33
33
  csv = csv_excel if csv_excel[0].count > 1
34
34
  end
35
35
 
36
36
  self[0]=Workbook::Sheet.new(csv,self) unless sheet.has_contents?
37
37
  end
38
-
38
+
39
39
  end
40
40
  end
41
41
  end
@@ -12,9 +12,9 @@ module Workbook
12
12
  styles = ""
13
13
  Zip::ZipFile.open(file_obj) do |zipfile|
14
14
  zipfile.entries.each do |file|
15
- styles = zipfile.read(file.name) if file.name == "styles.xml"
16
-
17
- content = zipfile.read(file.name) if file.name == "content.xml"
15
+ styles = zipfile.read(file.name) if file.name == "styles.xml"
16
+
17
+ content = zipfile.read(file.name) if file.name == "content.xml"
18
18
  end
19
19
  end
20
20
  content = Nokogiri.XML(content)
@@ -24,12 +24,12 @@ module Workbook
24
24
  parse_ods content
25
25
  return self
26
26
  end
27
-
27
+
28
28
  def set_format_property format, property, value
29
29
  value.strip!
30
30
  format[property] = value if value and value != ""
31
31
  end
32
-
32
+
33
33
  def parse_ods_style parse_ods_style
34
34
  parse_ods_style.xpath("//style:style").each do |style|
35
35
  style_family = style.xpath("@style:family").to_s
@@ -47,63 +47,109 @@ module Workbook
47
47
  end
48
48
  end
49
49
  end
50
-
50
+
51
51
  # updates self with and ods-type content.xml
52
52
  # @param [Nokogiri::XML::Document] ods_spreadsheet nokogirified content.xml
53
53
  # @return [Workbook::Book] self
54
54
  def parse_ods ods_spreadsheet=template.raws[Nokogiri::XML::Document], options={}
55
55
  require 'cgi'
56
-
56
+
57
57
  options = {:additional_type_parsing=>false}.merge options
58
58
  # styles
59
59
  #puts ods_spreadsheet
60
60
  parse_ods_style ods_spreadsheet
61
-
61
+
62
62
  # data
63
63
  ods_spreadsheet.xpath("//office:body/office:spreadsheet").each_with_index do |sheet,sheetindex|
64
- s = self.create_or_open_sheet_at(sheetindex)
64
+ workbook_sheet = self.create_or_open_sheet_at(sheetindex)
65
65
  sheet.xpath("table:table").each_with_index do |table,tableindex|
66
- t = s.create_or_open_table_at(tableindex)
67
- t.name = table.xpath("@table:name").to_s
68
- columns = table.xpath("table:table-column").count
69
- table.xpath("table:table-row").each_with_index do |row,rowindex|
70
- cells = row.xpath("table:table-cell|table:covered-table-cell")
71
- r = Workbook::Row.new
72
- columns.times do |column_index|
73
- cell = cells[column_index]
74
- c = Workbook::Cell.new()
75
- value = nil
76
- if cell
77
- if cell.name == "covered-table-cell"
78
- value = Workbook::NilValue.new(:covered)
79
- else
80
- cell_style_name = cell.xpath('@table:style-name').to_s
81
- c.format = self.template.formats[cell_style_name]
82
- valuetype = cell.xpath('@office:value-type').to_s
83
- c.colspan= cell.xpath('@table:number-columns-spanned').to_s
84
- c.rowspan= cell.xpath('@table:number-rows-spanned').to_s
85
-
86
- value = CGI.unescapeHTML(cell.xpath("text:p//text()").to_s)
87
- value = (value == "") ? nil : value
88
- case valuetype
89
- when 'float'
90
- value = cell.xpath("@office:value").to_s.to_f
91
- when 'integer'
92
- value = cell.xpath("@office:value").to_s.to_i
93
- when 'date'
94
- value = DateTime.parse(cell.xpath("@office:date-value").to_s)
95
- end
96
- end
97
- end
98
- c.value = value
99
- r << c
100
- end
101
- t << r
102
- end
66
+ parse_local_table(workbook_sheet,table,tableindex)
103
67
  end
104
68
  end
105
69
  return self
106
70
  end
71
+
72
+ #parse the contents of an entire table by parsing every row in it and adding it to the table
73
+ def parse_local_table(sheet,table,tableindex)
74
+ local_table = sheet.create_or_open_table_at(tableindex)
75
+ local_table.name = table.xpath("@table:name").to_s
76
+ #column_count = get_column_count(table)
77
+ table.xpath("table:table-row").each do |row|
78
+ local_table << parse_local_row(row)
79
+ end
80
+ local_table.trim!
81
+ end
82
+
83
+ #set column count
84
+ def get_column_count(table)
85
+ init_column_count = table.xpath("table:table-column").count
86
+ first_row = table.xpath("table:table-row").first
87
+ cells = first_row.xpath("table:table-cell|table:covered-table-cell")
88
+ column_count = 0
89
+ cells.each do |cell|
90
+ if cell.xpath('@table:number-columns-spanned').children.size>0
91
+ column_count +=cell.xpath('@table:number-columns-spanned').children[0].inner_text.to_i
92
+ else
93
+ column_count +=1
94
+ end
95
+ end
96
+ column_count
97
+ end
98
+
99
+ #parse the contents of an entire row by parsing every cell in it and adding it to the row
100
+ def parse_local_row(row)
101
+ cells = row.xpath("table:table-cell|table:covered-table-cell")
102
+ workbook_row = Workbook::Row.new
103
+ cells.each do |cell|
104
+ @cell = cell
105
+ repeat = get_repeat
106
+ workbook_cell = Workbook::Cell.new()
107
+ workbook_cell.value = @cell.nil? ? nil : parse_local_cell(workbook_cell)
108
+ repeat.times do
109
+ workbook_row << workbook_cell
110
+ end
111
+ end
112
+ return workbook_row
113
+ end
114
+
115
+ def get_repeat
116
+ pre_set = @cell.xpath('@table:number-columns-repeated').to_s
117
+ return 1 if (pre_set.nil? || pre_set=="") # if not present, don't repeat.
118
+ return 1 unless "#{pre_set.to_i}"=="#{pre_set}" # return 1 if it's not a valid integer
119
+ return 1 if pre_set.to_i < 1 # return 1, negative repeats make no sense
120
+ return pre_set.to_i
121
+ end
122
+
123
+ #parse the contents of a single cell
124
+ def parse_local_cell(workbook_cell)
125
+ return Workbook::NilValue.new(:covered) if @cell.name == "covered-table-cell"
126
+ set_cell_attributes(workbook_cell)
127
+ valuetype = @cell.xpath('@office:value-type').to_s
128
+ parse_local_value(valuetype)
129
+ end
130
+
131
+ # Sets cell attributes for rowspan, colspan and format
132
+ def set_cell_attributes(workbook_cell)
133
+ workbook_cell.format = self.template.formats[@cell.xpath('@table:style-name').to_s]
134
+ workbook_cell.colspan= @cell.xpath('@table:number-columns-spanned').to_s
135
+ workbook_cell.rowspan= @cell.xpath('@table:number-rows-spanned').to_s
136
+ end
137
+
138
+ # Sets value in right context type
139
+ def parse_local_value(valuetype)
140
+ value = CGI.unescapeHTML(@cell.xpath("text:p//text()").to_s)
141
+ value = (value=="") ? nil : value
142
+ case valuetype
143
+ when 'float'
144
+ value = @cell.xpath("@office:value").to_s.to_f
145
+ when 'integer'
146
+ value = @cell.xpath("@office:value").to_s.to_i
147
+ when 'date'
148
+ value = DateTime.parse(@cell.xpath("@office:date-value").to_s)
149
+ end
150
+ value
151
+ end
152
+
107
153
  end
108
154
  end
109
155
  end