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.
- 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 +16 -0
- data/Gemfile +4 -2
- data/README.md +9 -7
- data/Rakefile +9 -7
- data/lib/workbook/book.rb +74 -61
- data/lib/workbook/cell.rb +59 -12
- data/lib/workbook/column.rb +33 -29
- data/lib/workbook/format.rb +24 -23
- data/lib/workbook/generatetypes.rb +6 -5
- data/lib/workbook/modules/cache.rb +8 -7
- data/lib/workbook/modules/cell.rb +78 -99
- data/lib/workbook/modules/diff_sort.rb +93 -82
- data/lib/workbook/modules/raw_objects_storage.rb +7 -7
- data/lib/workbook/modules/type_parser.rb +31 -21
- data/lib/workbook/nil_value.rb +5 -9
- data/lib/workbook/readers/csv_reader.rb +7 -9
- data/lib/workbook/readers/ods_reader.rb +49 -49
- data/lib/workbook/readers/txt_reader.rb +7 -7
- data/lib/workbook/readers/xls_reader.rb +22 -32
- data/lib/workbook/readers/xls_shared.rb +107 -116
- data/lib/workbook/readers/xlsx_reader.rb +47 -46
- data/lib/workbook/row.rb +100 -83
- data/lib/workbook/sheet.rb +48 -37
- data/lib/workbook/table.rb +97 -71
- data/lib/workbook/template.rb +13 -14
- data/lib/workbook/types/date.rb +2 -1
- data/lib/workbook/types/false.rb +1 -1
- data/lib/workbook/types/false_class.rb +2 -1
- data/lib/workbook/types/nil.rb +1 -1
- data/lib/workbook/types/nil_class.rb +3 -2
- data/lib/workbook/types/numeric.rb +3 -2
- data/lib/workbook/types/string.rb +3 -2
- data/lib/workbook/types/time.rb +3 -2
- data/lib/workbook/types/true.rb +1 -1
- data/lib/workbook/types/true_class.rb +3 -2
- data/lib/workbook/version.rb +3 -2
- data/lib/workbook/writers/csv_table_writer.rb +11 -12
- data/lib/workbook/writers/html_writer.rb +35 -37
- data/lib/workbook/writers/json_table_writer.rb +9 -10
- data/lib/workbook/writers/xls_writer.rb +31 -35
- data/lib/workbook/writers/xlsx_writer.rb +46 -28
- data/lib/workbook.rb +17 -14
- data/test/helper.rb +8 -5
- data/test/test_book.rb +43 -38
- data/test/test_column.rb +29 -25
- data/test/test_format.rb +53 -55
- data/test/test_functional.rb +9 -8
- data/test/test_modules_cache.rb +20 -17
- data/test/test_modules_cell.rb +49 -46
- data/test/test_modules_table_diff_sort.rb +56 -63
- data/test/test_modules_type_parser.rb +63 -31
- data/test/test_readers_csv_reader.rb +50 -42
- data/test/test_readers_ods_reader.rb +30 -31
- data/test/test_readers_txt_reader.rb +23 -23
- data/test/test_readers_xls_reader.rb +22 -23
- data/test/test_readers_xls_shared.rb +5 -4
- data/test/test_readers_xlsx_reader.rb +46 -37
- data/test/test_row.rb +107 -109
- data/test/test_sheet.rb +43 -35
- data/test/test_table.rb +84 -60
- data/test/test_template.rb +18 -15
- data/test/test_types_date.rb +7 -7
- data/test/test_writers_csv_writer.rb +24 -0
- data/test/test_writers_html_writer.rb +44 -41
- data/test/test_writers_json_writer.rb +11 -9
- data/test/test_writers_xls_writer.rb +52 -35
- data/test/test_writers_xlsx_writer.rb +64 -34
- data/workbook.gemspec +26 -27
- metadata +93 -27
@@ -1,16 +1,18 @@
|
|
1
|
-
# -*- encoding : utf-8 -*-
|
2
1
|
# frozen_string_literal: true
|
3
|
-
|
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
|
-
|
11
|
+
def load_xlsm file_obj, options = {}
|
12
|
+
load_xlsx file_obj, options
|
12
13
|
end
|
13
|
-
|
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
|
23
|
-
sheets[file.name.sub(/^xl\//,
|
24
|
-
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)
|
25
27
|
shared_string_file = zipfile.read(file.name)
|
26
|
-
elsif
|
28
|
+
elsif /xl\/workbook.xml$/.match?(file.name)
|
27
29
|
workbook = zipfile.read(file.name)
|
28
|
-
elsif
|
30
|
+
elsif /xl\/_rels\/workbook.xml.rels$/.match?(file.name)
|
29
31
|
workbook_rels = zipfile.read(file.name)
|
30
|
-
elsif
|
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
|
-
|
54
|
-
|
54
|
+
push parse_xlsx_sheet(sheet)
|
55
|
+
last.name = name
|
55
56
|
end
|
56
57
|
end
|
57
58
|
|
58
59
|
@shared_strings = nil
|
59
|
-
|
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
|
70
|
-
backgrounds = extract_xlsx_backgrounds
|
71
|
-
|
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 =
|
84
|
-
if id >= 164
|
85
|
-
|
83
|
+
id = cellformat.attr("numFmtId").to_i
|
84
|
+
hash[:numberformat] = if id >= 164
|
85
|
+
custom_number_formats[id]
|
86
86
|
else
|
87
|
-
|
87
|
+
ms_formatting_to_strftime(id)
|
88
88
|
end
|
89
|
-
|
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
|
-
|
117
|
+
pattern_fill = fill.css("patternFill").first
|
118
118
|
# TODO: convert to html-hex
|
119
|
-
hash[:background] =
|
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(
|
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(
|
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(
|
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
|
-
|
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?
|
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
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
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[
|
175
|
+
fmt = template.formats[format_index]
|
177
176
|
|
178
|
-
|
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 =
|
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
|
5
|
+
class Row
|
5
6
|
include Workbook::Modules::Cache
|
7
|
+
include Enumerable
|
8
|
+
extend Forwardable
|
6
9
|
|
7
|
-
|
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 ? {:
|
18
|
-
cells = []
|
19
|
-
|
20
|
-
|
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)
|
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
|
56
|
+
table << self
|
60
57
|
end
|
61
58
|
end
|
62
59
|
|
63
|
-
# Add
|
64
|
-
# @param [Workbook::
|
65
|
-
def push(
|
66
|
-
|
67
|
-
|
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
|
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 +(
|
81
|
-
|
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
|
-
|
91
|
-
|
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 [
|
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
|
-
|
112
|
-
elsif index_or_hash.is_a?
|
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
|
-
|
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
|
-
|
119
|
-
|
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 [
|
132
|
-
# @param [String,
|
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?
|
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 =
|
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=
|
147
|
+
value_celled.value = value
|
155
148
|
end
|
156
149
|
value_celled.row = self
|
157
|
-
|
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
|
166
|
-
options = {:
|
167
|
-
|
168
|
-
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
|
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
|
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
|
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
|
-
|
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 =
|
213
|
+
values = @cells
|
221
214
|
hash = {}
|
222
|
-
keys.each_with_index {|k,i| hash[k]=values[i]}
|
223
|
-
|
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
|
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
|
-
|
245
|
-
|
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 =
|
244
|
+
a = header? ? 0 : 1
|
254
245
|
b = other.header? ? 0 : 1
|
255
|
-
return (a <=> b) if (a==0
|
256
|
-
|
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
|
-
|
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, {:
|
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
|
-
|
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 =
|
293
|
-
|
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
|
289
|
+
if desired_length && (index < desired_length)
|
296
290
|
break
|
297
|
-
elsif desired_length
|
298
|
-
|
291
|
+
elsif desired_length && (index >= desired_length)
|
292
|
+
delete_at(index)
|
299
293
|
elsif self[index].nil?
|
300
|
-
|
294
|
+
delete_at(index)
|
301
295
|
else
|
302
296
|
break
|
303
297
|
end
|
304
298
|
end
|
305
|
-
(desired_length -
|
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
|