workbook 0.8.0 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +21 -0
  3. data/.gitignore +4 -1
  4. data/.ruby-version +1 -1
  5. data/.travis.yml +4 -4
  6. data/CHANGELOG.md +16 -0
  7. data/Gemfile +4 -2
  8. data/README.md +9 -7
  9. data/Rakefile +9 -7
  10. data/lib/workbook/book.rb +74 -61
  11. data/lib/workbook/cell.rb +59 -12
  12. data/lib/workbook/column.rb +33 -29
  13. data/lib/workbook/format.rb +24 -23
  14. data/lib/workbook/generatetypes.rb +6 -5
  15. data/lib/workbook/modules/cache.rb +8 -7
  16. data/lib/workbook/modules/cell.rb +78 -99
  17. data/lib/workbook/modules/diff_sort.rb +93 -82
  18. data/lib/workbook/modules/raw_objects_storage.rb +7 -7
  19. data/lib/workbook/modules/type_parser.rb +31 -21
  20. data/lib/workbook/nil_value.rb +5 -9
  21. data/lib/workbook/readers/csv_reader.rb +7 -9
  22. data/lib/workbook/readers/ods_reader.rb +49 -49
  23. data/lib/workbook/readers/txt_reader.rb +7 -7
  24. data/lib/workbook/readers/xls_reader.rb +22 -32
  25. data/lib/workbook/readers/xls_shared.rb +107 -116
  26. data/lib/workbook/readers/xlsx_reader.rb +47 -46
  27. data/lib/workbook/row.rb +100 -83
  28. data/lib/workbook/sheet.rb +48 -37
  29. data/lib/workbook/table.rb +97 -71
  30. data/lib/workbook/template.rb +13 -14
  31. data/lib/workbook/types/date.rb +2 -1
  32. data/lib/workbook/types/false.rb +1 -1
  33. data/lib/workbook/types/false_class.rb +2 -1
  34. data/lib/workbook/types/nil.rb +1 -1
  35. data/lib/workbook/types/nil_class.rb +3 -2
  36. data/lib/workbook/types/numeric.rb +3 -2
  37. data/lib/workbook/types/string.rb +3 -2
  38. data/lib/workbook/types/time.rb +3 -2
  39. data/lib/workbook/types/true.rb +1 -1
  40. data/lib/workbook/types/true_class.rb +3 -2
  41. data/lib/workbook/version.rb +3 -2
  42. data/lib/workbook/writers/csv_table_writer.rb +11 -12
  43. data/lib/workbook/writers/html_writer.rb +35 -37
  44. data/lib/workbook/writers/json_table_writer.rb +9 -10
  45. data/lib/workbook/writers/xls_writer.rb +31 -35
  46. data/lib/workbook/writers/xlsx_writer.rb +46 -28
  47. data/lib/workbook.rb +17 -14
  48. data/test/helper.rb +8 -5
  49. data/test/test_book.rb +43 -38
  50. data/test/test_column.rb +29 -25
  51. data/test/test_format.rb +53 -55
  52. data/test/test_functional.rb +9 -8
  53. data/test/test_modules_cache.rb +20 -17
  54. data/test/test_modules_cell.rb +49 -46
  55. data/test/test_modules_table_diff_sort.rb +56 -63
  56. data/test/test_modules_type_parser.rb +63 -31
  57. data/test/test_readers_csv_reader.rb +50 -42
  58. data/test/test_readers_ods_reader.rb +30 -31
  59. data/test/test_readers_txt_reader.rb +23 -23
  60. data/test/test_readers_xls_reader.rb +22 -23
  61. data/test/test_readers_xls_shared.rb +5 -4
  62. data/test/test_readers_xlsx_reader.rb +46 -37
  63. data/test/test_row.rb +107 -109
  64. data/test/test_sheet.rb +43 -35
  65. data/test/test_table.rb +84 -60
  66. data/test/test_template.rb +18 -15
  67. data/test/test_types_date.rb +7 -7
  68. data/test/test_writers_csv_writer.rb +24 -0
  69. data/test/test_writers_html_writer.rb +44 -41
  70. data/test/test_writers_json_writer.rb +11 -9
  71. data/test/test_writers_xls_writer.rb +52 -35
  72. data/test/test_writers_xlsx_writer.rb +64 -34
  73. data/workbook.gemspec +26 -27
  74. metadata +93 -27
@@ -1,16 +1,18 @@
1
- # -*- encoding : utf-8 -*-
2
1
  # frozen_string_literal: true
3
- require 'workbook/readers/xls_shared'
2
+ # frozen_string_literal: true
3
+
4
+ require "workbook/readers/xls_shared"
4
5
 
5
6
  module Workbook
6
7
  module Readers
7
8
  module XlsxReader
8
9
  include Workbook::Readers::XlsShared
9
10
 
10
- def load_xlsm file_obj, options={}
11
- self.load_xlsx file_obj, options
11
+ def load_xlsm file_obj, options = {}
12
+ load_xlsx file_obj, options
12
13
  end
13
- def load_xlsx file_obj, options={}
14
+
15
+ def load_xlsx file_obj, options = {}
14
16
  file_obj = file_obj.path if file_obj.is_a? File
15
17
  sheets = {}
16
18
  shared_string_file = ""
@@ -19,15 +21,15 @@ module Workbook
19
21
  workbook_rels = ""
20
22
  Zip::File.open(file_obj) do |zipfile|
21
23
  zipfile.entries.each do |file|
22
- if file.name.match(/^xl\/worksheets\/(.*)\.xml$/)
23
- sheets[file.name.sub(/^xl\//,'')] = zipfile.read(file.name)
24
- elsif file.name.match(/xl\/sharedStrings.xml$/)
24
+ if /^xl\/worksheets\/(.*)\.xml$/.match?(file.name)
25
+ sheets[file.name.sub(/^xl\//, "")] = zipfile.read(file.name)
26
+ elsif /xl\/sharedStrings.xml$/.match?(file.name)
25
27
  shared_string_file = zipfile.read(file.name)
26
- elsif file.name.match(/xl\/workbook.xml$/)
28
+ elsif /xl\/workbook.xml$/.match?(file.name)
27
29
  workbook = zipfile.read(file.name)
28
- elsif file.name.match(/xl\/_rels\/workbook.xml.rels$/)
30
+ elsif /xl\/_rels\/workbook.xml.rels$/.match?(file.name)
29
31
  workbook_rels = zipfile.read(file.name)
30
- elsif file.name.match(/xl\/styles.xml$/)
32
+ elsif /xl\/styles.xml$/.match?(file.name)
31
33
  styles = zipfile.read(file.name)
32
34
  end
33
35
  # content = zipfile.read(file.name) if file.name == "content.xml"
@@ -36,10 +38,9 @@ module Workbook
36
38
 
37
39
  parse_xlsx_styles(styles)
38
40
 
39
-
40
41
  relation_file = {}
41
42
  Nokogiri::XML(workbook_rels).css("Relationships Relationship").each do |relship|
42
- relation_file[relship.attr("Id")]=relship.attr("Target")
43
+ relation_file[relship.attr("Id")] = relship.attr("Target")
43
44
  end
44
45
 
45
46
  @shared_strings = parse_shared_string_file(shared_string_file)
@@ -50,13 +51,13 @@ module Workbook
50
51
  state = sheet.attr("state")
51
52
  if state != "hidden"
52
53
  sheet = sheets[filename]
53
- self.push parse_xlsx_sheet(sheet)
54
- self.last.name = name
54
+ push parse_xlsx_sheet(sheet)
55
+ last.name = name
55
56
  end
56
57
  end
57
58
 
58
59
  @shared_strings = nil
59
- self.each do |sheet|
60
+ each do |sheet|
60
61
  sheet.each do |table|
61
62
  table.trim!
62
63
  end
@@ -66,10 +67,9 @@ module Workbook
66
67
  def parse_xlsx_styles(styles)
67
68
  styles = Nokogiri::XML(styles)
68
69
 
69
- fonts = parse_xlsx_fonts styles
70
- backgrounds = extract_xlsx_backgrounds styles
71
- customNumberFormats = extract_xlsx_number_formats styles
72
-
70
+ fonts = parse_xlsx_fonts(styles)
71
+ backgrounds = extract_xlsx_backgrounds(styles)
72
+ custom_number_formats = extract_xlsx_number_formats(styles)
73
73
 
74
74
  styles.css("cellXfs xf").each do |cellformat|
75
75
  hash = {}
@@ -80,13 +80,13 @@ module Workbook
80
80
  font_hash = fonts[cellformat.attr("applyFill").to_i]
81
81
  hash.merge!(font_hash) if font_hash
82
82
 
83
- id = (cellformat.attr("numFmtId")).to_i
84
- if id >= 164
85
- hash[:numberformat] = customNumberFormats[id]
83
+ id = cellformat.attr("numFmtId").to_i
84
+ hash[:numberformat] = if id >= 164
85
+ custom_number_formats[id]
86
86
  else
87
- hash[:numberformat] = ms_formatting_to_strftime(id)
87
+ ms_formatting_to_strftime(id)
88
88
  end
89
- self.template.add_format Workbook::Format.new(hash)
89
+ template.add_format(Workbook::Format.new(hash))
90
90
  end
91
91
  end
92
92
 
@@ -114,69 +114,67 @@ module Workbook
114
114
  def extract_xlsx_backgrounds styles
115
115
  styles.css("fills fill").collect do |fill|
116
116
  hash = {}
117
- patternFill = fill.css("patternFill").first
117
+ pattern_fill = fill.css("patternFill").first
118
118
  # TODO: convert to html-hex
119
- hash[:background] = patternFill.attr("patternType") if patternFill
119
+ hash[:background] = pattern_fill.attr("patternType") if pattern_fill
120
120
  hash
121
121
  end
122
122
  end
123
123
 
124
124
  def parse_shared_string_file file
125
- Nokogiri::XML(file).css("sst si").collect{|a| a.text}
125
+ Nokogiri::XML(file).css("sst si").collect { |a| a.text }
126
126
  end
127
+
127
128
  def parse_xlsx_sheet sheet_xml
128
129
  sheet = Workbook::Sheet.new
129
130
  table = sheet.table
130
131
 
131
132
  noko_xml = Nokogiri::XML(sheet_xml)
132
133
 
133
- rows = noko_xml.css('sheetData row').collect{|row| parse_xlsx_row(row)}
134
+ rows = noko_xml.css("sheetData row").collect { |row| parse_xlsx_row(row) }
134
135
  rows.each do |row|
135
136
  table << row
136
137
  end
137
138
 
138
- columns = noko_xml.css('cols col').collect{|col| parse_xlsx_column(col) }
139
+ columns = noko_xml.css("cols col").collect { |col| parse_xlsx_column(col) }
139
140
  table.columns = columns
140
141
 
141
142
  sheet
142
143
  end
143
144
 
144
145
  def parse_xlsx_column column
145
- col = Workbook::Column.new()
146
+ col = Workbook::Column.new
146
147
  col.width = column.attr("width").to_f
147
148
  col
148
149
  end
150
+
149
151
  def parse_xlsx_row row
150
- cells_with_pos = row.css('c').collect{|a| parse_xlsx_cell(a)}
152
+ cells_with_pos = row.css("c").collect { |a| parse_xlsx_cell(a) }
151
153
  row = Workbook::Row.new
152
154
  cells_with_pos.each do |cell_with_pos|
153
155
  position = cell_with_pos[:position]
154
156
  col = position.match(/^[A-Z]*/).to_s
155
157
  row[col] = cell_with_pos[:cell]
156
158
  end
157
- row = pad_xlsx_row(row)
158
- row
159
+ pad_xlsx_row(row)
159
160
  end
160
161
 
161
162
  def pad_xlsx_row(row)
162
163
  row.each_with_index do |cell, index|
163
- row[index]=Workbook::Cell.new(nil) if cell.nil? and !cell.is_a?(Workbook::Cell)
164
+ row[index] = Workbook::Cell.new(nil) if cell.nil? && !cell.is_a?(Workbook::Cell)
164
165
  end
165
166
  row
166
167
  end
167
168
 
168
169
  def parse_xlsx_cell cell
169
- # style_id = cell.attr('s')
170
- # p cell
171
- type = cell.attr('t')
172
- formatIndex = cell.attr('s').to_i
173
- position = cell.attr('r')
174
- formula = cell.css('f').text()
170
+ type = cell.attr("t")
171
+ format_index = cell.attr("s").to_i
172
+ position = cell.attr("r")
173
+ formula = cell.css("f").text
175
174
  value = cell.text
176
- fmt = template.formats[formatIndex]
175
+ fmt = template.formats[format_index]
177
176
 
178
- # puts type
179
- if type == "n" or type == nil
177
+ if (type == "n") || type.nil?
180
178
  if fmt.derived_type == :date
181
179
  value = xls_number_to_date(value)
182
180
  elsif fmt.derived_type == :time
@@ -186,7 +184,7 @@ module Workbook
186
184
  elsif formula == "FALSE()"
187
185
  value = false
188
186
  elsif type == "n"
189
- value = value.match(/\./) ? value.to_f : value.to_i
187
+ value = /\./.match?(value) ? value.to_f : value.to_i
190
188
  end
191
189
  elsif type == "b"
192
190
  if value.to_s == "0"
@@ -197,12 +195,15 @@ module Workbook
197
195
  elsif type == "s"
198
196
  value = @shared_strings[value.to_i]
199
197
  end
198
+
200
199
  cell = Workbook::Cell.new(value, format: fmt)
201
200
  cell.formula = formula
201
+
202
202
  {cell: cell, position: position}
203
203
  end
204
+
204
205
  def parse_xlsx
205
206
  end
206
207
  end
207
208
  end
208
- end
209
+ end
data/lib/workbook/row.rb CHANGED
@@ -1,31 +1,28 @@
1
- # -*- encoding : utf-8 -*-
2
1
  # frozen_string_literal: true
2
+ # frozen_string_literal: true
3
+
3
4
  module Workbook
4
- class Row < Array
5
+ class Row
5
6
  include Workbook::Modules::Cache
7
+ include Enumerable
8
+ extend Forwardable
6
9
 
7
- alias_method :compare_without_header, :<=>
8
- attr_accessor :placeholder # The placeholder attribute is used in compares (corresponds to newly created or removed lines (depending which side you're on)
10
+ attr_accessor :placeholder # The placeholder attribute is used in compares (corresponds to newly created or removed lines (depending which side you're on)
9
11
  attr_accessor :format
12
+ attr_reader :cells
13
+
14
+ delegate [:first, :last, :pop, :delete_at, :each, :count, :include?, :index, :delete_if, :to_csv, :length, :empty?] => :@cells
10
15
 
11
16
  # Initialize a new row
12
17
  #
13
18
  # @param [Workbook::Row, Array<Workbook::Cell>, Array] cells list of cells to initialize the row with, default is empty
14
19
  # @param [Workbook::Table] table a row normally belongs to a table, reference it here
15
20
  # @param [Hash] options Supported options: parse_cells_on_batch_creation (parse cell values during row-initalization, default: false), cell_parse_options (default {}, see Workbook::Modules::TypeParser)
16
- def initialize cells=[], table=nil, options={}
17
- options=options ? {:parse_cells_on_batch_creation=>false,:cell_parse_options=>{},:clone_cells=>false}.merge(options) : {}
18
- cells = [] if cells==nil
19
- self.table= table
20
- cells.each do |c|
21
- if c.is_a? Workbook::Cell
22
- c = c.clone if options[:clone_cells]
23
- else
24
- c = Workbook::Cell.new(c, {row:self})
25
- c.parse!(options[:cell_parse_options]) if options[:parse_cells_on_batch_creation]
26
- end
27
- push c
28
- end
21
+ def initialize cells = [], table = nil, options = {}
22
+ options = options ? {parse_cells_on_batch_creation: false, cell_parse_options: {}, clone_cells: false}.merge(options) : {}
23
+ @cells = []
24
+ add_cells(cells, options)
25
+ self.table = table
29
26
  end
30
27
 
31
28
  # An internal function used in diffs
@@ -53,45 +50,43 @@ module Workbook
53
50
  #
54
51
  # @param [Workbook::Table] t the table this row belongs to
55
52
  def table= t
56
- raise ArgumentError, "table should be a Workbook::Table (you passed a #{t.class})" unless t.is_a?(Workbook::Table) or t == nil
53
+ raise ArgumentError, "table should be a Workbook::Table (you passed a #{t.class})" unless t.is_a?(Workbook::Table) || t.nil?
57
54
  if t
58
55
  @table = t
59
- table.push(self) #unless table.index(self) and self.placeholder?
56
+ table << self
60
57
  end
61
58
  end
62
59
 
63
- # Add cell
64
- # @param [Workbook::Cell, Numeric,String,Time,Date,TrueClass,FalseClass,NilClass] cell or value to add
65
- def push(cell)
66
- cell = Workbook::Cell.new(cell, {row:self}) unless cell.class == Workbook::Cell
67
- super(cell)
60
+ # Add row
61
+ # @param [Workbook::Table, Array] row(s) to add
62
+ def push(*args)
63
+ args.each do |arg|
64
+ self.<<(arg)
65
+ end
68
66
  end
67
+ alias_method :append, :push
69
68
 
70
69
  # Add cell
71
70
  # @param [Workbook::Cell, Numeric,String,Time,Date,TrueClass,FalseClass,NilClass] cell or value to add
72
71
  def <<(cell)
73
- cell = Workbook::Cell.new(cell, {row:self}) unless cell.class == Workbook::Cell
74
- super(cell)
72
+ @cells << (cell.is_a?(Workbook::Cell) ? cell : Workbook::Cell.new(cell, {row: self}))
75
73
  end
76
74
 
77
75
  # plus
78
76
  # @param [Workbook::Row, Array] row to add
79
77
  # @return [Workbook::Row] a new row, not linked to the table
80
- def +(row)
81
- rv = super(row)
82
- rv = Workbook::Row.new(rv) unless rv.class == Workbook::Row
83
- return rv
78
+ def +(other)
79
+ Workbook::Row.new(@cells + Row.new(other).cells)
84
80
  end
85
81
 
86
82
  # concat
87
83
  # @param [Workbook::Row, Array] row to add
88
84
  # @return [self] self
89
85
  def concat(row)
90
- row = Workbook::Row.new(row) unless row.class == Workbook::Row
91
- super(row)
86
+ @cells.concat(Workbook::Row.new(row).cells)
87
+ self
92
88
  end
93
89
 
94
-
95
90
  # Overrides normal Array's []-function with support for symbols that identify a column based on the header-values and / or
96
91
  #
97
92
  # @example Lookup using fixnum or header value encoded as symbol
@@ -99,7 +94,7 @@ module Workbook
99
94
  # row["A"] #=> <Cell value="a">
100
95
  # row[:a] #=> <Cell value="a">
101
96
  #
102
- # @param [Fixnum, Symbol, String] index_or_hash that identifies the column (strings are converted to symbols)
97
+ # @param [Integer, Symbol, String] index_or_hash that identifies the column (strings are converted to symbols)
103
98
  # @return [Workbook::Cell, nil]
104
99
  def [](index_or_hash)
105
100
  if index_or_hash.is_a? Symbol
@@ -108,17 +103,15 @@ module Workbook
108
103
  rv = to_hash[index_or_hash]
109
104
  rescue NoMethodError
110
105
  end
111
- return rv
112
- elsif index_or_hash.is_a? String and index_or_hash.match(/^[A-Z]*$/)
106
+ rv
107
+ elsif index_or_hash.is_a?(String) && index_or_hash.match(/^[A-Z]*$/)
113
108
  # it looks like a column indicator
114
- return to_a[Workbook::Column.alpha_index_to_number_index(index_or_hash)]
109
+ @cells[Workbook::Column.alpha_index_to_number_index(index_or_hash)]
115
110
  elsif index_or_hash.is_a? String
116
- symbolized = Workbook::Cell.new(index_or_hash, {row:self}).to_sym
111
+ symbolized = Workbook::Cell.new(index_or_hash, {row: self}).to_sym
117
112
  self[symbolized]
118
- else
119
- if index_or_hash
120
- return to_a[index_or_hash]
121
- end
113
+ elsif index_or_hash
114
+ @cells[index_or_hash]
122
115
  end
123
116
  end
124
117
 
@@ -128,18 +121,18 @@ module Workbook
128
121
  # row[1] #=> <Cell value="a">
129
122
  # row[:a] #=> <Cell value="a">
130
123
  #
131
- # @param [Fixnum, Symbol, String] index_or_hash that identifies the column
132
- # @param [String, Fixnum, NilClass, Date, DateTime, Time, Float] value
124
+ # @param [Integer, Symbol, String] index_or_hash that identifies the column
125
+ # @param [String, Integer, NilClass, Date, DateTime, Time, Float] value
133
126
  # @return [Workbook::Cell, nil]
134
127
  def []= index_or_hash, value
135
128
  index = index_or_hash
136
129
  if index_or_hash.is_a? Symbol
137
130
  index = table_header_keys.index(index_or_hash)
138
- elsif index_or_hash.is_a? String and index_or_hash.match(/^[A-Z]*$/)
131
+ elsif index_or_hash.is_a?(String) && index_or_hash.match(/^[A-Z]*$/)
139
132
  # it looks like a column indicator
140
133
  index = Workbook::Column.alpha_index_to_number_index(index_or_hash)
141
134
  elsif index_or_hash.is_a? String
142
- symbolized = Workbook::Cell.new(index_or_hash, {row:self}).to_sym
135
+ symbolized = Workbook::Cell.new(index_or_hash, {row: self}).to_sym
143
136
  index = table_header_keys.index(symbolized)
144
137
  end
145
138
 
@@ -147,14 +140,14 @@ module Workbook
147
140
  if value.is_a? Workbook::Cell
148
141
  value_celled = value
149
142
  else
150
- current_cell = self[index]
143
+ current_cell = @cells[index]
151
144
  if current_cell.is_a? Workbook::Cell
152
145
  value_celled = current_cell
153
146
  end
154
- value_celled.value=(value)
147
+ value_celled.value = value
155
148
  end
156
149
  value_celled.row = self
157
- super(index,value_celled)
150
+ @cells[index] = value_celled
158
151
  end
159
152
 
160
153
  # Returns an array of cells allows you to find cells by a given color, normally a string containing a hex
@@ -162,10 +155,10 @@ module Workbook
162
155
  # @param [String] color a CSS-style hex-string
163
156
  # @param [Hash] options Option :hash_keys (default true) returns row as an array of symbols
164
157
  # @return [Array<Symbol>, Workbook::Row<Workbook::Cell>]
165
- def find_cells_by_background_color color=:any, options={}
166
- options = {:hash_keys=>true}.merge(options)
167
- cells = self.collect {|c| c if c.format.has_background_color?(color) }.compact
168
- r = Row.new cells
158
+ def find_cells_by_background_color color = :any, options = {}
159
+ options = {hash_keys: true}.merge(options)
160
+ found_cells = @cells.collect { |c| c if c.format.has_background_color?(color) }.compact
161
+ r = Row.new found_cells
169
162
  options[:hash_keys] ? r.to_symbols : r
170
163
  end
171
164
 
@@ -173,35 +166,35 @@ module Workbook
173
166
  #
174
167
  # @return [Boolean]
175
168
  def header?
176
- table != nil and self.object_id == table_header.object_id
169
+ !table.nil? && (object_id == table_header.object_id)
177
170
  end
178
171
 
179
172
  # Is this the first row in the table
180
173
  #
181
174
  # @return [Boolean, NilClass] returns nil if it doesn't belong to a table, false when it isn't the first row of a table and true when it is.
182
175
  def first?
183
- table != nil and self.object_id == table.first.object_id
176
+ !table.nil? && (object_id == table.first.object_id)
184
177
  end
185
178
 
186
179
  # Returns true when all the cells in the row have values whose to_s value equals an empty string
187
180
  #
188
181
  # @return [Boolean]
189
182
  def no_values?
190
- all? {|c| c.value.to_s == ''}
183
+ @cells.all? { |c| c.value.to_s == "" }
191
184
  end
192
185
 
193
186
  # Converts a row to an array of symbol representations of the row content, see also: Workbook::Cell#to_sym
194
187
  # @return [Array<Symbol>] returns row as an array of symbols
195
188
  def to_symbols
196
- fetch_cache(:to_symbols){
197
- collect{|c| c.to_sym}
189
+ fetch_cache(:to_symbols) {
190
+ @cells.collect { |c| c.to_sym }
198
191
  }
199
192
  end
200
193
 
201
194
  # Converts the row to an array of Workbook::Cell's
202
195
  # @return [Array<Workbook::Cell>] returns row as an array of symbols
203
196
  def to_a
204
- self.collect{|c| c}
197
+ @cells
205
198
  end
206
199
 
207
200
  def table_header
@@ -217,17 +210,17 @@ module Workbook
217
210
  # @return [Hash]
218
211
  def to_hash
219
212
  keys = table_header_keys
220
- values = self
213
+ values = @cells
221
214
  hash = {}
222
- keys.each_with_index {|k,i| hash[k]=values[i]}
223
- return hash
215
+ keys.each_with_index { |k, i| hash[k] = values[i] }
216
+ hash
224
217
  end
225
218
 
226
219
  # Quick assessor to the book's template, if it exists
227
220
  #
228
221
  # @return [Workbook::Template]
229
222
  def template
230
- table.template if table
223
+ table&.template
231
224
  end
232
225
 
233
226
  # Returns a hash representation of this row
@@ -238,11 +231,9 @@ module Workbook
238
231
  # @return [Hash]
239
232
 
240
233
  def to_hash_with_values
241
- keys = table_header_keys
242
- values = self
243
234
  @hash_with_values = {}
244
- keys.each_with_index {|k,i| v=values[i]; v=v.value if v; @hash_with_values[k]=v}
245
- return @hash_with_values
235
+ table_header_keys.each_with_index { |k, i| @hash_with_values[k] = @cells[i]&.value }
236
+ @hash_with_values
246
237
  end
247
238
 
248
239
  # Compares one row wiht another
@@ -250,10 +241,14 @@ module Workbook
250
241
  # @param [Workbook::Row] other row to compare against
251
242
  # @return [Workbook::Row] a row with the diff result.
252
243
  def <=> other
253
- a = self.header? ? 0 : 1
244
+ a = header? ? 0 : 1
254
245
  b = other.header? ? 0 : 1
255
- return (a <=> b) if (a==0 or b==0)
256
- compare_without_header other
246
+ return (a <=> b) if (a == 0) || (b == 0)
247
+ @cells <=> other.cells
248
+ end
249
+
250
+ def == other
251
+ @cells.map(&:value) == other.is_a?(Workbook::Row) ? other.cells.map(&:value) : other
257
252
  end
258
253
 
259
254
  # The first cell of the row is considered to be the key
@@ -265,45 +260,67 @@ module Workbook
265
260
 
266
261
  # Compact detaches the row from the table
267
262
  def compact
268
- r = self.clone
269
- r = r.collect{|c| c unless c.nil?}.compact
263
+ clone.collect { |c| c unless c.nil? }.compact
270
264
  end
271
265
 
272
266
  # clone the row with together with the cells
273
267
  #
274
268
  # @return [Workbook::Row] a cloned copy of self with cells
275
269
  def clone
276
- Workbook::Row.new(self, nil, {:clone_cells=>true})
270
+ Workbook::Row.new(self, nil, {clone_cells: true})
277
271
  end
278
272
 
279
273
  # remove all the trailing nil-cells (returning a trimmed clone)
280
274
  #
281
275
  # @param [Integer] desired_length of the new row
282
276
  # @return [Workbook::Row] a trimmed clone of the array
283
- def trim(desired_length=nil)
284
- self.clone.trim!(desired_length)
277
+ def trim(desired_length = nil)
278
+ clone.trim!(desired_length)
285
279
  end
286
280
 
287
281
  # remove all the trailing nil-cells (returning a trimmed self)
288
282
  #
289
283
  # @param [Integer] desired_length of the new row
290
284
  # @return [Workbook::Row] self
291
- def trim!(desired_length=nil)
292
- self_count = self.count-1
293
- self.count.times do |index|
285
+ def trim!(desired_length = nil)
286
+ self_count = count - 1
287
+ count.times do |index|
294
288
  index = self_count - index
295
- if desired_length and index < desired_length
289
+ if desired_length && (index < desired_length)
296
290
  break
297
- elsif desired_length and index >= desired_length
298
- self.delete_at(index)
291
+ elsif desired_length && (index >= desired_length)
292
+ delete_at(index)
299
293
  elsif self[index].nil?
300
- self.delete_at(index)
294
+ delete_at(index)
301
295
  else
302
296
  break
303
297
  end
304
298
  end
305
- (desired_length - self.count).times{|a| self << (Workbook::Cell.new(nil))} if desired_length and (desired_length - self.count) > 0
299
+ (desired_length - count).times { |a| @cells << Workbook::Cell.new(nil) } if desired_length && ((desired_length - count) > 0)
306
300
  self
307
301
  end
302
+
303
+ private
304
+
305
+ # Only to be used on initialization
306
+ # @param [Workbook::Row, Array<Workbook::Cell>, Array] cells list of cells to initialize the row with, default is empty
307
+ # @param [Hash] options Supported options: parse_cells_on_batch_creation (parse cell values during row-initalization, default: false), cell_parse_options (default {}, see Workbook::Modules::TypeParser)
308
+ def add_cells cells = [], options = {}
309
+ return nil if cells.nil?
310
+ cells.each do |c|
311
+ @cells << (prepare_cell(c, options))
312
+ end
313
+ end
314
+
315
+ def prepare_cell cell, options
316
+ if cell.is_a? Workbook::Cell
317
+ cell = cell.clone if options[:clone_cells]
318
+ else
319
+ cell = Workbook::Cell.new(cell, {row: self})
320
+ cell.parse!(options[:cell_parse_options]) if options[:parse_cells_on_batch_creation]
321
+ end
322
+
323
+ cell
324
+ end
308
325
  end
309
326
  end