workbook 0.8.0 → 0.8.2

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 (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