workbook 0.8.1 → 0.9.0

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