workbook 0.8.0 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|