workbook 0.8.1 → 0.9.0

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