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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +21 -0
- data/.gitignore +4 -1
- data/.ruby-version +1 -1
- data/.travis.yml +4 -4
- data/CHANGELOG.md +8 -0
- data/Gemfile +2 -2
- data/README.md +9 -7
- data/Rakefile +6 -6
- data/json_test.json +1 -0
- data/lib/workbook/book.rb +73 -62
- data/lib/workbook/cell.rb +58 -13
- data/lib/workbook/column.rb +31 -28
- data/lib/workbook/format.rb +23 -24
- data/lib/workbook/generatetypes.rb +4 -4
- data/lib/workbook/modules/cache.rb +6 -7
- data/lib/workbook/modules/cell.rb +77 -100
- data/lib/workbook/modules/diff_sort.rb +92 -83
- data/lib/workbook/modules/raw_objects_storage.rb +6 -8
- data/lib/workbook/modules/type_parser.rb +30 -22
- data/lib/workbook/nil_value.rb +4 -9
- data/lib/workbook/readers/csv_reader.rb +7 -10
- data/lib/workbook/readers/ods_reader.rb +51 -50
- data/lib/workbook/readers/txt_reader.rb +6 -8
- data/lib/workbook/readers/xls_reader.rb +21 -33
- data/lib/workbook/readers/xls_shared.rb +106 -117
- data/lib/workbook/readers/xlsx_reader.rb +45 -46
- data/lib/workbook/row.rb +99 -84
- data/lib/workbook/sheet.rb +47 -38
- data/lib/workbook/table.rb +96 -72
- data/lib/workbook/template.rb +12 -15
- data/lib/workbook/types/false.rb +0 -1
- data/lib/workbook/types/nil.rb +0 -1
- data/lib/workbook/types/nil_class.rb +1 -1
- data/lib/workbook/types/numeric.rb +1 -1
- data/lib/workbook/types/string.rb +1 -1
- data/lib/workbook/types/time.rb +1 -1
- data/lib/workbook/types/true.rb +0 -1
- data/lib/workbook/types/true_class.rb +1 -1
- data/lib/workbook/version.rb +2 -3
- data/lib/workbook/writers/csv_table_writer.rb +10 -13
- data/lib/workbook/writers/html_writer.rb +34 -38
- data/lib/workbook/writers/json_table_writer.rb +8 -11
- data/lib/workbook/writers/xls_writer.rb +30 -36
- data/lib/workbook/writers/xlsx_writer.rb +45 -29
- data/lib/workbook.rb +16 -15
- data/test/artifacts/currency_test.ods +0 -0
- data/test/helper.rb +6 -5
- data/test/test_book.rb +41 -38
- data/test/test_column.rb +26 -24
- data/test/test_format.rb +51 -55
- data/test/test_functional.rb +7 -8
- data/test/test_modules_cache.rb +18 -17
- data/test/test_modules_cell.rb +55 -46
- data/test/test_modules_table_diff_sort.rb +55 -64
- data/test/test_modules_type_parser.rb +61 -31
- data/test/test_readers_csv_reader.rb +48 -42
- data/test/test_readers_ods_reader.rb +36 -31
- data/test/test_readers_txt_reader.rb +21 -23
- data/test/test_readers_xls_reader.rb +20 -23
- data/test/test_readers_xls_shared.rb +2 -3
- data/test/test_readers_xlsx_reader.rb +44 -37
- data/test/test_row.rb +105 -109
- data/test/test_sheet.rb +35 -41
- data/test/test_table.rb +82 -60
- data/test/test_template.rb +16 -15
- data/test/test_types_date.rb +4 -6
- data/test/test_writers_csv_writer.rb +24 -0
- data/test/test_writers_html_writer.rb +42 -41
- data/test/test_writers_json_writer.rb +16 -9
- data/test/test_writers_xls_writer.rb +50 -35
- data/test/test_writers_xlsx_writer.rb +62 -34
- data/workbook.gemspec +25 -27
- 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
|
-
|
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
|
-
|
11
|
+
def load_xlsm file_obj, options = {}
|
12
|
+
load_xlsx file_obj, options
|
14
13
|
end
|
15
|
-
|
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
|
25
|
-
sheets[file.name.sub(/^xl\//,
|
26
|
-
elsif
|
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
|
28
|
+
elsif /xl\/workbook.xml$/.match?(file.name)
|
29
29
|
workbook = zipfile.read(file.name)
|
30
|
-
elsif
|
30
|
+
elsif /xl\/_rels\/workbook.xml.rels$/.match?(file.name)
|
31
31
|
workbook_rels = zipfile.read(file.name)
|
32
|
-
elsif
|
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
|
-
|
56
|
-
|
54
|
+
push parse_xlsx_sheet(sheet)
|
55
|
+
last.name = name
|
57
56
|
end
|
58
57
|
end
|
59
58
|
|
60
59
|
@shared_strings = nil
|
61
|
-
|
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
|
72
|
-
backgrounds = extract_xlsx_backgrounds
|
73
|
-
|
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 =
|
86
|
-
if id >= 164
|
87
|
-
|
83
|
+
id = cellformat.attr("numFmtId").to_i
|
84
|
+
hash[:numberformat] = if id >= 164
|
85
|
+
custom_number_formats[id]
|
88
86
|
else
|
89
|
-
|
87
|
+
ms_formatting_to_strftime(id)
|
90
88
|
end
|
91
|
-
|
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
|
-
|
117
|
+
pattern_fill = fill.css("patternFill").first
|
120
118
|
# TODO: convert to html-hex
|
121
|
-
hash[:background] =
|
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(
|
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(
|
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(
|
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
|
-
|
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?
|
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
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
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[
|
175
|
+
fmt = template.formats[format_index]
|
179
176
|
|
180
|
-
|
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 =
|
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
|
5
|
+
class Row
|
7
6
|
include Workbook::Modules::Cache
|
7
|
+
include Enumerable
|
8
|
+
extend Forwardable
|
8
9
|
|
9
|
-
|
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 ? {:
|
20
|
-
cells = []
|
21
|
-
|
22
|
-
|
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)
|
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
|
56
|
+
table << self
|
62
57
|
end
|
63
58
|
end
|
64
59
|
|
65
|
-
# Add
|
66
|
-
# @param [Workbook::
|
67
|
-
def push(
|
68
|
-
|
69
|
-
|
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
|
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 +(
|
83
|
-
|
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
|
-
|
93
|
-
|
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 [
|
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
|
-
|
114
|
-
elsif index_or_hash.is_a?
|
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
|
-
|
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
|
-
|
121
|
-
|
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 [
|
134
|
-
# @param [String,
|
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?
|
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 =
|
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=
|
147
|
+
value_celled.value = value
|
157
148
|
end
|
158
149
|
value_celled.row = self
|
159
|
-
|
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
|
168
|
-
options = {:
|
169
|
-
|
170
|
-
r = Row.new
|
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
|
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
|
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
|
-
|
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 =
|
213
|
+
values = @cells
|
223
214
|
hash = {}
|
224
|
-
keys.each_with_index {|k,i| hash[k]=values[i]}
|
225
|
-
|
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
|
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
|
-
|
247
|
-
|
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 =
|
244
|
+
a = header? ? 0 : 1
|
256
245
|
b = other.header? ? 0 : 1
|
257
|
-
return (a <=> b) if (a==0
|
258
|
-
|
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
|
-
|
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, {:
|
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
|
-
|
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 =
|
295
|
-
|
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
|
289
|
+
if desired_length && (index < desired_length)
|
298
290
|
break
|
299
|
-
elsif desired_length
|
300
|
-
|
291
|
+
elsif desired_length && (index >= desired_length)
|
292
|
+
delete_at(index)
|
301
293
|
elsif self[index].nil?
|
302
|
-
|
294
|
+
delete_at(index)
|
303
295
|
else
|
304
296
|
break
|
305
297
|
end
|
306
298
|
end
|
307
|
-
(desired_length -
|
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
|