keeguon-spreadsheet 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +619 -0
- data/Manifest.txt +85 -0
- data/bin/xlsopcodes +18 -0
- data/lib/parseexcel.rb +27 -0
- data/lib/parseexcel/parseexcel.rb +75 -0
- data/lib/parseexcel/parser.rb +11 -0
- data/lib/spreadsheet.rb +80 -0
- data/lib/spreadsheet/column.rb +71 -0
- data/lib/spreadsheet/compatibility.rb +23 -0
- data/lib/spreadsheet/datatypes.rb +161 -0
- data/lib/spreadsheet/encodings.rb +57 -0
- data/lib/spreadsheet/excel.rb +88 -0
- data/lib/spreadsheet/excel/error.rb +26 -0
- data/lib/spreadsheet/excel/internals.rb +458 -0
- data/lib/spreadsheet/excel/internals/biff5.rb +17 -0
- data/lib/spreadsheet/excel/internals/biff8.rb +19 -0
- data/lib/spreadsheet/excel/offset.rb +41 -0
- data/lib/spreadsheet/excel/password_hash.rb +24 -0
- data/lib/spreadsheet/excel/reader.rb +1302 -0
- data/lib/spreadsheet/excel/reader/biff5.rb +42 -0
- data/lib/spreadsheet/excel/reader/biff8.rb +231 -0
- data/lib/spreadsheet/excel/rgb.rb +122 -0
- data/lib/spreadsheet/excel/row.rb +98 -0
- data/lib/spreadsheet/excel/sst_entry.rb +46 -0
- data/lib/spreadsheet/excel/workbook.rb +80 -0
- data/lib/spreadsheet/excel/worksheet.rb +115 -0
- data/lib/spreadsheet/excel/writer.rb +1 -0
- data/lib/spreadsheet/excel/writer/biff8.rb +75 -0
- data/lib/spreadsheet/excel/writer/format.rb +264 -0
- data/lib/spreadsheet/excel/writer/n_worksheet.rb +888 -0
- data/lib/spreadsheet/excel/writer/workbook.rb +735 -0
- data/lib/spreadsheet/excel/writer/worksheet.rb +940 -0
- data/lib/spreadsheet/font.rb +115 -0
- data/lib/spreadsheet/format.rb +209 -0
- data/lib/spreadsheet/formula.rb +9 -0
- data/lib/spreadsheet/helpers.rb +11 -0
- data/lib/spreadsheet/link.rb +43 -0
- data/lib/spreadsheet/note.rb +23 -0
- data/lib/spreadsheet/noteObject.rb +17 -0
- data/lib/spreadsheet/row.rb +151 -0
- data/lib/spreadsheet/workbook.rb +143 -0
- data/lib/spreadsheet/worksheet.rb +326 -0
- data/lib/spreadsheet/writer.rb +30 -0
- data/test/data/test_adding_data_to_existing_file.xls +0 -0
- data/test/data/test_borders.xls +0 -0
- data/test/data/test_changes.xls +0 -0
- data/test/data/test_comment.xls +0 -0
- data/test/data/test_copy.xls +0 -0
- data/test/data/test_datetime.xls +0 -0
- data/test/data/test_empty.xls +0 -0
- data/test/data/test_formula.xls +0 -0
- data/test/data/test_long_sst_record.xls +0 -0
- data/test/data/test_margin.xls +0 -0
- data/test/data/test_merged_and_protected.xls +0 -0
- data/test/data/test_merged_cells.xls +0 -0
- data/test/data/test_missing_row.xls +0 -0
- data/test/data/test_pagesetup.xls +0 -0
- data/test/data/test_version_excel5.xls +0 -0
- data/test/data/test_version_excel95.xls +0 -0
- data/test/data/test_version_excel97.xls +0 -0
- data/test/data/test_version_excel97_2010.xls +0 -0
- data/test/data/test_worksheet_visibility.xls +0 -0
- data/test/excel/reader.rb +30 -0
- data/test/excel/row.rb +40 -0
- data/test/excel/writer/workbook.rb +95 -0
- data/test/excel/writer/worksheet.rb +81 -0
- data/test/font.rb +163 -0
- data/test/format.rb +95 -0
- data/test/integration.rb +1390 -0
- data/test/row.rb +33 -0
- data/test/suite.rb +18 -0
- data/test/workbook.rb +55 -0
- data/test/workbook_protection.rb +19 -0
- data/test/worksheet.rb +112 -0
- metadata +148 -0
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'spreadsheet/format'
|
2
|
+
require 'spreadsheet/encodings'
|
3
|
+
|
4
|
+
module Spreadsheet
|
5
|
+
##
|
6
|
+
# The Workbook class represents a Spreadsheet-Document and is the entry point
|
7
|
+
# for all Spreadsheet manipulation.
|
8
|
+
#
|
9
|
+
# Interesting Attributes:
|
10
|
+
# #default_format:: The default format used for all cells in this Workbook.
|
11
|
+
# that have no format set explicitly or in
|
12
|
+
# Row#default_format or Worksheet#default_format.
|
13
|
+
class Workbook
|
14
|
+
include Spreadsheet::Encodings
|
15
|
+
attr_reader :io, :worksheets, :formats, :fonts, :palette
|
16
|
+
attr_accessor :active_worksheet, :encoding, :default_format, :version
|
17
|
+
def initialize io = nil, opts={:default_format => Format.new}
|
18
|
+
@worksheets = []
|
19
|
+
@io = io
|
20
|
+
@fonts = []
|
21
|
+
@palette = {}
|
22
|
+
@formats = []
|
23
|
+
@formats_set = {}
|
24
|
+
if @default_format = opts[:default_format]
|
25
|
+
add_format @default_format
|
26
|
+
end
|
27
|
+
end
|
28
|
+
##
|
29
|
+
# Add a Font to the Workbook. Used by the parser. You should not need to
|
30
|
+
# use this Method.
|
31
|
+
def add_font font
|
32
|
+
@fonts.push(font).uniq! if font
|
33
|
+
font
|
34
|
+
end
|
35
|
+
##
|
36
|
+
# Add a Format to the Workbook. If you use Row#set_format, you should not
|
37
|
+
# need to use this Method.
|
38
|
+
def add_format format
|
39
|
+
if format && !@formats_set[format]
|
40
|
+
@formats_set[format] = true
|
41
|
+
@formats.push(format)
|
42
|
+
end
|
43
|
+
format
|
44
|
+
end
|
45
|
+
##
|
46
|
+
# Add a Worksheet to the Workbook.
|
47
|
+
def add_worksheet worksheet
|
48
|
+
worksheet.workbook = self
|
49
|
+
@worksheets.push worksheet
|
50
|
+
worksheet
|
51
|
+
end
|
52
|
+
##
|
53
|
+
# Delete a Worksheet from Workbook by it's index
|
54
|
+
def delete_worksheet worksheet_index
|
55
|
+
@worksheets.delete_at worksheet_index
|
56
|
+
end
|
57
|
+
##
|
58
|
+
# Change the RGB components of the elements in the colour palette.
|
59
|
+
def set_custom_color idx, red, green, blue
|
60
|
+
raise 'Invalid format' if [red, green, blue].find { |c| ! (0..255).include?(c) }
|
61
|
+
|
62
|
+
@palette[idx] = [red, green, blue]
|
63
|
+
end
|
64
|
+
##
|
65
|
+
# Create a new Worksheet in this Workbook.
|
66
|
+
# Used without options this creates a Worksheet with the name 'WorksheetN'
|
67
|
+
# where the new Worksheet is the Nth Worksheet in this Workbook.
|
68
|
+
#
|
69
|
+
# Use the option <em>:name => 'My pretty Name'</em> to override this
|
70
|
+
# behavior.
|
71
|
+
def create_worksheet opts = {}
|
72
|
+
opts[:name] ||= client("Worksheet#{@worksheets.size.next}", 'UTF-8')
|
73
|
+
add_worksheet Worksheet.new(opts)
|
74
|
+
end
|
75
|
+
##
|
76
|
+
# Returns the count of total worksheets present.
|
77
|
+
# Takes no arguments. Just returns the length of @worksheets array.
|
78
|
+
def sheet_count
|
79
|
+
@worksheets.length
|
80
|
+
end
|
81
|
+
##
|
82
|
+
# The Font at _idx_
|
83
|
+
def font idx
|
84
|
+
@fonts[idx]
|
85
|
+
end
|
86
|
+
##
|
87
|
+
# The Format at _idx_, or - if _idx_ is a String -
|
88
|
+
# the Format with name == _idx_
|
89
|
+
def format idx
|
90
|
+
case idx
|
91
|
+
when Integer
|
92
|
+
@formats[idx] || @default_format
|
93
|
+
when String
|
94
|
+
@formats.find do |fmt| fmt.name == idx end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
def inspect
|
98
|
+
variables = (instance_variables - uninspect_variables).collect do |name|
|
99
|
+
"%s=%s" % [name, instance_variable_get(name)]
|
100
|
+
end.join(' ')
|
101
|
+
uninspect = uninspect_variables.collect do |name|
|
102
|
+
var = instance_variable_get name
|
103
|
+
"%s=%s[%i]" % [name, var.class, var.size]
|
104
|
+
end.join(' ')
|
105
|
+
sprintf "#<%s:0x%014x %s %s>", self.class, object_id,
|
106
|
+
variables, uninspect
|
107
|
+
end
|
108
|
+
def uninspect_variables # :nodoc:
|
109
|
+
%w{@formats @fonts @worksheets}
|
110
|
+
end
|
111
|
+
##
|
112
|
+
# The Worksheet at _idx_, or - if _idx_ is a String -
|
113
|
+
# the Worksheet with name == _idx_
|
114
|
+
def worksheet idx
|
115
|
+
case idx
|
116
|
+
when Integer
|
117
|
+
@worksheets[idx]
|
118
|
+
when String
|
119
|
+
@worksheets.find do |sheet| sheet.name == idx end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
##
|
123
|
+
# Write this Workbook to a File, IO Stream or Writer Object. The latter will
|
124
|
+
# make more sense once there are more than just an Excel-Writer available.
|
125
|
+
def write io_path_or_writer
|
126
|
+
if io_path_or_writer.is_a? Writer
|
127
|
+
io_path_or_writer.write self
|
128
|
+
else
|
129
|
+
writer(io_path_or_writer).write(self)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
##
|
133
|
+
# Returns a new instance of the default Writer class for this Workbook (can
|
134
|
+
# only be an Excel::Writer::Workbook at this time)
|
135
|
+
def writer io_or_path, type=Excel, version=self.version
|
136
|
+
if type == Excel
|
137
|
+
Excel::Writer::Workbook.new io_or_path
|
138
|
+
else
|
139
|
+
raise NotImplementedError, "No Writer defined for #{type}"
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,326 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'spreadsheet/column'
|
3
|
+
require 'spreadsheet/encodings'
|
4
|
+
require 'spreadsheet/row'
|
5
|
+
require 'spreadsheet/excel/password_hash'
|
6
|
+
|
7
|
+
module Spreadsheet
|
8
|
+
##
|
9
|
+
# The Worksheet class. Contains most of the Spreadsheet data in Rows.
|
10
|
+
#
|
11
|
+
# Interesting Attributes
|
12
|
+
# #name :: The Name of this Worksheet.
|
13
|
+
# #default_format:: The default format used for all cells in this Workhseet
|
14
|
+
# that have no format set explicitly or in
|
15
|
+
# Row#default_format.
|
16
|
+
# #rows :: The Rows in this Worksheet. It is not recommended to
|
17
|
+
# Manipulate this Array directly. If you do, call
|
18
|
+
# #updated_from with the smallest modified index.
|
19
|
+
# #columns :: The Column formatting in this Worksheet. Column
|
20
|
+
# instances may appear at more than one position in #columns.
|
21
|
+
# If you modify a Column directly, your changes will be
|
22
|
+
# reflected in all those positions.
|
23
|
+
# #selected :: When a user chooses to print a Workbook, Excel will include
|
24
|
+
# all selected Worksheets. If no Worksheet is selected at
|
25
|
+
# Workbook#write, then the first Worksheet is selected by
|
26
|
+
# default.
|
27
|
+
class Worksheet
|
28
|
+
include Spreadsheet::Encodings
|
29
|
+
include Spreadsheet::Datatypes
|
30
|
+
include Enumerable
|
31
|
+
attr_accessor :name, :selected, :workbook, :password_hash
|
32
|
+
attr_reader :rows, :columns, :merged_cells, :margins, :pagesetup
|
33
|
+
enum :visibility, :visible, :hidden, :strong_hidden
|
34
|
+
def initialize opts={}
|
35
|
+
@default_format = nil
|
36
|
+
@selected = opts[:selected]
|
37
|
+
@dimensions = [0,0,0,0]
|
38
|
+
@pagesetup = {
|
39
|
+
:orig_data => [9, 100, 1, 1, 1, 0, 300, 300, 0.5, 0.5, 1],
|
40
|
+
:orientation => :portrait,
|
41
|
+
:adjust_to => 100
|
42
|
+
}
|
43
|
+
@margins = {
|
44
|
+
:top => 1,
|
45
|
+
:left => 0.75,
|
46
|
+
:right => 0.75,
|
47
|
+
:bottom => 1
|
48
|
+
}
|
49
|
+
@name = opts[:name] || 'Worksheet'
|
50
|
+
@workbook = opts[:workbook]
|
51
|
+
@rows = []
|
52
|
+
@columns = []
|
53
|
+
@links = {}
|
54
|
+
@merged_cells = []
|
55
|
+
@protected = false
|
56
|
+
@password_hash = 0
|
57
|
+
@visibility = opts[:visibility]
|
58
|
+
end
|
59
|
+
def active # :nodoc:
|
60
|
+
warn "Worksheet#active is deprecated. Please use Worksheet#selected instead."
|
61
|
+
selected
|
62
|
+
end
|
63
|
+
def active= selected # :nodoc:
|
64
|
+
warn "Worksheet#active= is deprecated. Please use Worksheet#selected= instead."
|
65
|
+
self.selected = selected
|
66
|
+
end
|
67
|
+
##
|
68
|
+
# Add a Format to the Workbook. If you use Row#set_format, you should not
|
69
|
+
# need to use this Method.
|
70
|
+
def add_format fmt
|
71
|
+
@workbook.add_format fmt if fmt
|
72
|
+
end
|
73
|
+
##
|
74
|
+
# Get the enriched value of the Cell at _row_, _column_.
|
75
|
+
# See also Worksheet#[], Row#[].
|
76
|
+
def cell row, column
|
77
|
+
row(row)[column]
|
78
|
+
end
|
79
|
+
##
|
80
|
+
# Returns the Column at _idx_.
|
81
|
+
def column idx
|
82
|
+
@columns[idx] || Column.new(idx, default_format, :worksheet => self)
|
83
|
+
end
|
84
|
+
##
|
85
|
+
# The number of columns in this Worksheet which contain data.
|
86
|
+
def column_count
|
87
|
+
dimensions[3] - dimensions[2]
|
88
|
+
end
|
89
|
+
def column_updated idx, column
|
90
|
+
@columns[idx] = column
|
91
|
+
end
|
92
|
+
##
|
93
|
+
# Delete the Row at _idx_ (0-based) from this Worksheet.
|
94
|
+
def delete_row idx
|
95
|
+
res = @rows.delete_at idx
|
96
|
+
updated_from idx
|
97
|
+
res
|
98
|
+
end
|
99
|
+
##
|
100
|
+
# The default Format of this Worksheet, if you have set one.
|
101
|
+
# Returns the Workbook's default Format otherwise.
|
102
|
+
def default_format
|
103
|
+
@default_format || @workbook.default_format
|
104
|
+
end
|
105
|
+
##
|
106
|
+
# Set the default Format of this Worksheet.
|
107
|
+
def default_format= format
|
108
|
+
@default_format = format
|
109
|
+
add_format format
|
110
|
+
format
|
111
|
+
end
|
112
|
+
##
|
113
|
+
# Is the worksheet protected?
|
114
|
+
def protected?
|
115
|
+
@protected
|
116
|
+
end
|
117
|
+
##
|
118
|
+
# Set worklist protection
|
119
|
+
def protect! password = ''
|
120
|
+
@protected = true
|
121
|
+
password = password.to_s
|
122
|
+
if password.size == 0
|
123
|
+
@password_hash = 0
|
124
|
+
else
|
125
|
+
@password_hash = Excel::Password.password_hash password
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
##
|
130
|
+
# Dimensions:: [ first used row, first unused row,
|
131
|
+
# first used column, first unused column ]
|
132
|
+
# ( First used means that all rows or columns before that are
|
133
|
+
# empty. First unused means that this and all following rows
|
134
|
+
# or columns are empty. )
|
135
|
+
def dimensions
|
136
|
+
@dimensions || recalculate_dimensions
|
137
|
+
end
|
138
|
+
##
|
139
|
+
# If no argument is given, #each iterates over all used Rows (from the first
|
140
|
+
# used Row until but omitting the first unused Row, see also #dimensions).
|
141
|
+
#
|
142
|
+
# If the argument skip is given, #each iterates from that row until but
|
143
|
+
# omitting the first unused Row, effectively skipping the first _skip_ Rows
|
144
|
+
# from the top of the Worksheet.
|
145
|
+
def each skip=dimensions[0]
|
146
|
+
skip.upto(dimensions[1] - 1) do |idx|
|
147
|
+
yield row(idx)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
def encoding # :nodoc:
|
151
|
+
@workbook.encoding
|
152
|
+
end
|
153
|
+
##
|
154
|
+
# Sets the default Format of the column at _idx_.
|
155
|
+
#
|
156
|
+
# _idx_ may be an Integer, or an Enumerable that iterates over a number of
|
157
|
+
# Integers.
|
158
|
+
#
|
159
|
+
# _format_ is a Format, or nil if you want to remove the Formatting at _idx_
|
160
|
+
#
|
161
|
+
# Returns an instance of Column if _idx_ is an Integer, an Array of Columns
|
162
|
+
# otherwise.
|
163
|
+
def format_column idx, format=nil, opts={}
|
164
|
+
opts[:worksheet] = self
|
165
|
+
res = case idx
|
166
|
+
when Integer
|
167
|
+
column = nil
|
168
|
+
if format
|
169
|
+
column = Column.new(idx, format, opts)
|
170
|
+
end
|
171
|
+
@columns[idx] = column
|
172
|
+
else
|
173
|
+
idx.collect do |col| format_column col, format, opts end
|
174
|
+
end
|
175
|
+
shorten @columns
|
176
|
+
res
|
177
|
+
end
|
178
|
+
##
|
179
|
+
# Formats all Date, DateTime and Time cells with _format_ or the default
|
180
|
+
# formats:
|
181
|
+
# - 'DD.MM.YYYY' for Date
|
182
|
+
# - 'DD.MM.YYYY hh:mm:ss' for DateTime and Time
|
183
|
+
def format_dates! format=nil
|
184
|
+
new_formats = {}
|
185
|
+
fmt_str_time = client('DD.MM.YYYY hh:mm:ss', 'UTF-8')
|
186
|
+
fmt_str_date = client('DD.MM.YYYY', 'UTF-8')
|
187
|
+
each do |row|
|
188
|
+
row.each_with_index do |value, idx|
|
189
|
+
unless row.formats[idx] || row.format(idx).date_or_time?
|
190
|
+
numfmt = case value
|
191
|
+
when DateTime, Time
|
192
|
+
format || fmt_str_time
|
193
|
+
when Date
|
194
|
+
format || fmt_str_date
|
195
|
+
end
|
196
|
+
case numfmt
|
197
|
+
when Format
|
198
|
+
row.set_format idx, numfmt
|
199
|
+
when String
|
200
|
+
existing_format = row.format(idx)
|
201
|
+
new_formats[existing_format] ||= {}
|
202
|
+
new_format = new_formats[existing_format][numfmt]
|
203
|
+
if !new_format
|
204
|
+
new_format = new_formats[existing_format][numfmt] = existing_format.dup
|
205
|
+
new_format.number_format = numfmt
|
206
|
+
end
|
207
|
+
row.set_format idx, new_format
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
##
|
214
|
+
# Insert a Row at _idx_ (0-based) containing _cells_
|
215
|
+
def insert_row idx, cells=[]
|
216
|
+
res = @rows.insert idx, Row.new(self, idx, cells)
|
217
|
+
updated_from idx
|
218
|
+
res
|
219
|
+
end
|
220
|
+
def inspect
|
221
|
+
names = instance_variables
|
222
|
+
names.delete '@rows'
|
223
|
+
variables = names.collect do |name|
|
224
|
+
"%s=%s" % [name, instance_variable_get(name)]
|
225
|
+
end.join(' ')
|
226
|
+
sprintf "#<%s:0x%014x %s @rows[%i]>", self.class, object_id,
|
227
|
+
variables, row_count
|
228
|
+
end
|
229
|
+
## The last Row containing any data
|
230
|
+
def last_row
|
231
|
+
row(last_row_index)
|
232
|
+
end
|
233
|
+
## The index of the last Row containing any data
|
234
|
+
def last_row_index
|
235
|
+
[dimensions[1] - 1, 0].max
|
236
|
+
end
|
237
|
+
##
|
238
|
+
# Replace the Row at _idx_ with the following arguments. Like #update_row,
|
239
|
+
# but truncates the Row if there are fewer arguments than Cells in the Row.
|
240
|
+
def replace_row idx, *cells
|
241
|
+
if(row = @rows[idx]) && cells.size < row.size
|
242
|
+
cells.concat Array.new(row.size - cells.size)
|
243
|
+
end
|
244
|
+
update_row idx, *cells
|
245
|
+
end
|
246
|
+
##
|
247
|
+
# The Row at _idx_ or a new Row.
|
248
|
+
def row idx
|
249
|
+
@rows[idx] || Row.new(self, idx)
|
250
|
+
end
|
251
|
+
##
|
252
|
+
# The number of Rows in this Worksheet which contain data.
|
253
|
+
def row_count
|
254
|
+
dimensions[1] - dimensions[0]
|
255
|
+
end
|
256
|
+
##
|
257
|
+
# Tell Worksheet that the Row at _idx_ has been updated and the #dimensions
|
258
|
+
# need to be recalculated. You should not need to call this directly.
|
259
|
+
def row_updated idx, row
|
260
|
+
@dimensions = nil
|
261
|
+
@rows[idx] = row
|
262
|
+
end
|
263
|
+
##
|
264
|
+
# Updates the Row at _idx_ with the following arguments.
|
265
|
+
def update_row idx, *cells
|
266
|
+
res = if row = @rows[idx]
|
267
|
+
row[0, cells.size] = cells
|
268
|
+
row
|
269
|
+
else
|
270
|
+
Row.new self, idx, cells
|
271
|
+
end
|
272
|
+
row_updated idx, res
|
273
|
+
res
|
274
|
+
end
|
275
|
+
##
|
276
|
+
# Renumbers all Rows starting at _idx_ and calls #row_updated for each of
|
277
|
+
# them.
|
278
|
+
def updated_from index
|
279
|
+
index.upto(@rows.size - 1) do |idx|
|
280
|
+
row = row(idx)
|
281
|
+
row.idx = idx
|
282
|
+
row_updated idx, row
|
283
|
+
end
|
284
|
+
end
|
285
|
+
##
|
286
|
+
# Get the enriched value of the Cell at _row_, _column_.
|
287
|
+
# See also Worksheet#cell, Row#[].
|
288
|
+
def [] row, column
|
289
|
+
row(row)[column]
|
290
|
+
end
|
291
|
+
##
|
292
|
+
# Set the value of the Cell at _row_, _column_ to _value_.
|
293
|
+
# See also Row#[]=.
|
294
|
+
def []= row, column, value
|
295
|
+
row(row)[column] = value
|
296
|
+
end
|
297
|
+
##
|
298
|
+
# Merges multiple cells into one.
|
299
|
+
def merge_cells start_row, start_col, end_row, end_col
|
300
|
+
# FIXME enlarge or dup check
|
301
|
+
@merged_cells.push [start_row, end_row, start_col, end_col]
|
302
|
+
end
|
303
|
+
private
|
304
|
+
def index_of_first ary # :nodoc:
|
305
|
+
return unless ary
|
306
|
+
ary.index(ary.find do |elm| elm end)
|
307
|
+
end
|
308
|
+
def recalculate_dimensions # :nodoc:
|
309
|
+
shorten @rows
|
310
|
+
@dimensions = []
|
311
|
+
@dimensions[0] = index_of_first(@rows) || 0
|
312
|
+
@dimensions[1] = @rows.size
|
313
|
+
compact = @rows.compact
|
314
|
+
@dimensions[2] = compact.collect do |row| row.first_used end.compact.min || 0
|
315
|
+
@dimensions[3] = compact.collect do |row| row.first_unused end.max || 0
|
316
|
+
@dimensions
|
317
|
+
end
|
318
|
+
def shorten ary # :nodoc:
|
319
|
+
return unless ary
|
320
|
+
while !ary.empty? && !ary.last
|
321
|
+
ary.pop
|
322
|
+
end
|
323
|
+
ary unless ary.empty?
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|