workbook 0.4.2 → 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
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