workbook 0.8.1 → 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 +7 -0
- data/Gemfile +2 -2
- data/README.md +9 -7
- data/Rakefile +6 -6
- 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 +48 -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/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 +47 -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 +28 -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 +41 -35
- 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 +9 -9
- data/test/test_writers_xls_writer.rb +50 -35
- data/test/test_writers_xlsx_writer.rb +62 -34
- data/workbook.gemspec +26 -27
- metadata +92 -27
@@ -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
|