spreadbase 0.1.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,27 +1,6 @@
1
- # encoding: UTF-8
2
-
3
- =begin
4
- Copyright 2012 Saverio Miroddi saverio.pub2 <a-hat!> gmail.com
5
-
6
- This file is part of SpreadBase.
7
-
8
- SpreadBase is free software: you can redistribute it and/or modify it under the
9
- terms of the GNU Lesser General Public License as published by the Free Software
10
- Foundation, either version 3 of the License, or (at your option) any later
11
- version.
12
-
13
- SpreadBase is distributed in the hope that it will be useful, but WITHOUT ANY
14
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15
- PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
16
-
17
- You should have received a copy of the GNU Lesser General Public License along
18
- with SpreadBase. If not, see <http://www.gnu.org/licenses/>.
19
- =end
20
-
21
1
  require 'rexml/document'
22
2
  require 'date'
23
3
  require 'bigdecimal'
24
- require 'iconv' if RUBY_VERSION < '1.9'
25
4
 
26
5
  module SpreadBase # :nodoc:
27
6
 
@@ -81,17 +60,17 @@ module SpreadBase # :nodoc:
81
60
 
82
61
  # Returns the XML root node
83
62
  #
84
- def encode_to_document_node( el_document, options={} )
85
- root_node = REXML::Document.new( BASE_CONTENT_XML )
86
- spreadsheet_node = root_node.elements[ '//office:document-content/office:body/office:spreadsheet' ]
87
- styles_node = root_node.elements[ '//office:document-content/office:automatic-styles' ]
63
+ def encode_to_document_node(el_document)
64
+ root_node = REXML::Document.new(BASE_CONTENT_XML)
65
+ spreadsheet_node = root_node.elements['//office:document-content/office:body/office:spreadsheet']
66
+ styles_node = root_node.elements['//office:document-content/office:automatic-styles']
88
67
 
89
68
  el_document.column_width_styles.each do | style_name, column_width |
90
- encode_style( styles_node, style_name, column_width )
69
+ encode_style(styles_node, style_name, column_width)
91
70
  end
92
71
 
93
72
  el_document.tables.each do | table |
94
- encode_table( table, spreadsheet_node, options )
73
+ encode_table(table, spreadsheet_node)
95
74
  end
96
75
 
97
76
  root_node
@@ -99,91 +78,83 @@ module SpreadBase # :nodoc:
99
78
 
100
79
  # Currently only encodes column width styles
101
80
  #
102
- def encode_style( styles_node, style_name, column_width )
103
- style_node = styles_node.add_element( 'style:style', 'style:name' => style_name, 'style:family' => 'table-column' )
81
+ def encode_style(styles_node, style_name, column_width)
82
+ style_node = styles_node.add_element('style:style', 'style:name' => style_name, 'style:family' => 'table-column')
104
83
 
105
- style_node.add_element( 'style:table-column-properties', 'style:column-width' => column_width )
84
+ style_node.add_element('style:table-column-properties', 'style:column-width' => column_width)
106
85
  end
107
86
 
108
- def encode_table( table, spreadsheet_node, options={} )
109
- table_node = spreadsheet_node.add_element( 'table:table' )
87
+ def encode_table(table, spreadsheet_node)
88
+ table_node = spreadsheet_node.add_element('table:table')
110
89
 
111
- table_node.attributes[ 'table:name' ] = table.name
90
+ table_node.attributes['table:name'] = table.name
112
91
 
113
92
  table.column_width_styles.each do | style_name |
114
- encode_column( table_node, style_name ) if style_name
93
+ encode_column(table_node, style_name) if style_name
115
94
  end
116
95
 
117
96
  # At least one column element is required
118
97
  #
119
- table_node.add_element( 'table:table-column' ) if table.column_width_styles.size == 0
98
+ table_node.add_element('table:table-column') if table.column_width_styles.size == 0
120
99
 
121
- table.data.each do | row |
122
- encode_row( row, table_node, options )
100
+ table.data(as_cell: true).each do | row |
101
+ encode_row(row, table_node)
123
102
  end
124
103
  end
125
104
 
126
105
  # Currently only encodes column width styles
127
106
  #
128
- def encode_column( table_node, style_name )
129
- table_node.add_element( 'table:table-column', 'table:style-name' => style_name )
107
+ def encode_column(table_node, style_name)
108
+ table_node.add_element('table:table-column', 'table:style-name' => style_name)
130
109
  end
131
110
 
132
- def encode_row( row, table_node, options={} )
133
- row_node = table_node.add_element( 'table:table-row' )
111
+ def encode_row(row, table_node)
112
+ row_node = table_node.add_element('table:table-row')
134
113
 
135
- row.each do | value |
136
- encode_cell( value, row_node, options )
114
+ row.each do | cell |
115
+ encode_cell(cell.value, row_node)
137
116
  end
138
117
  end
139
118
 
140
- def encode_cell( value, row_node, options={} )
141
- force_18_strings_encoding = options[ :force_18_strings_encoding ] || 'UTF-8'
142
-
143
- cell_node = row_node.add_element( 'table:table-cell' )
119
+ def encode_cell(value, row_node)
120
+ cell_node = row_node.add_element('table:table-cell')
144
121
 
145
122
  # WATCH OUT!!! DateTime.new.is_a?( Date )!!!
146
123
  #
147
124
  case value
148
125
  when String
149
- cell_node.attributes[ 'office:value-type' ] = 'string'
150
-
151
- cell_value_node = cell_node.add_element( 'text:p' )
126
+ cell_node.attributes['office:value-type'] = 'string'
152
127
 
153
- if RUBY_VERSION >= '1.9'
154
- value = value.encode( 'UTF-8' )
155
- else
156
- value = Iconv.conv( 'UTF-8', force_18_strings_encoding, value )
157
- end
128
+ cell_value_node = cell_node.add_element('text:p')
158
129
 
159
- cell_value_node.text = value
130
+ cell_value_node.text = value.encode('UTF-8')
160
131
  when Time, DateTime
161
- cell_node.attributes[ 'office:value-type' ] = 'date'
162
- cell_node.attributes[ 'table:style-name' ] = 'datetime'
132
+ cell_node.attributes['office:value-type'] = 'date'
133
+ cell_node.attributes['table:style-name'] = 'datetime'
163
134
 
164
- encoded_value = value.strftime( '%Y-%m-%dT%H:%M:%S' )
135
+ encoded_value = value.strftime('%Y-%m-%dT%H:%M:%S')
165
136
 
166
- cell_node.attributes[ 'office:date-value' ] = encoded_value
137
+ cell_node.attributes['office:date-value'] = encoded_value
167
138
  when Date
168
- cell_node.attributes[ 'office:value-type' ] = 'date'
169
- cell_node.attributes[ 'table:style-name' ] = 'date'
139
+ cell_node.attributes['office:value-type'] = 'date'
140
+ cell_node.attributes['table:style-name'] = 'date'
170
141
 
171
- encoded_value = value.strftime( '%Y-%m-%d' )
142
+ encoded_value = value.strftime('%Y-%m-%d')
172
143
 
173
- cell_node.attributes[ 'office:date-value' ] = encoded_value
144
+ cell_node.attributes['office:date-value'] = encoded_value
174
145
  when BigDecimal
175
- cell_node.attributes[ 'office:value-type' ] = 'float'
146
+ cell_node.attributes['office:value-type'] = 'float'
176
147
 
177
- cell_node.attributes[ 'office:value' ] = value.to_s( 'F' )
178
- when Float, Fixnum
179
- cell_node.attributes[ 'office:value-type' ] = 'float'
148
+ cell_node.attributes['office:value'] = value.to_s('F')
149
+ when Float, Integer
150
+ cell_node.attributes['office:value-type'] = 'float'
180
151
 
181
- cell_node.attributes[ 'office:value' ] = value.to_s
152
+ cell_node.attributes['office:value'] = value.to_s
182
153
  when true, false
183
- cell_node.attributes[ 'office:value-type' ] = 'boolean'
184
- cell_node.attributes[ 'table:style-name' ] = 'boolean'
154
+ cell_node.attributes['office:value-type'] = 'boolean'
155
+ cell_node.attributes['table:style-name'] = 'boolean'
185
156
 
186
- cell_node.attributes[ 'office:boolean-value' ] = value.to_s
157
+ cell_node.attributes['office:boolean-value'] = value.to_s
187
158
  when nil
188
159
  # do nothing
189
160
  else
@@ -1,23 +1,3 @@
1
- # encoding: UTF-8
2
-
3
- =begin
4
- Copyright 2012 Saverio Miroddi saverio.pub2 <a-hat!> gmail.com
5
-
6
- This file is part of SpreadBase.
7
-
8
- SpreadBase is free software: you can redistribute it and/or modify it under the
9
- terms of the GNU Lesser General Public License as published by the Free Software
10
- Foundation, either version 3 of the License, or (at your option) any later
11
- version.
12
-
13
- SpreadBase is distributed in the hope that it will be useful, but WITHOUT ANY
14
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15
- PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
16
-
17
- You should have received a copy of the GNU Lesser General Public License along
18
- with SpreadBase. If not, see <http://www.gnu.org/licenses/>.
19
- =end
20
-
21
1
  module SpreadBase # :nodoc:
22
2
 
23
3
  # Represents the abstraction of a document, merging both the file and the
@@ -42,16 +22,15 @@ module SpreadBase # :nodoc:
42
22
  #
43
23
  # _options_:
44
24
  #
45
- # +force_18_strings_encoding+:: ('UTF-8') on ruby 1.8, when converting to UTF-8, assume the strings are using the specified format.
46
25
  # +floats_as_bigdecimal+:: (false) decode floats as BigDecimal instead of Float
47
26
  #
48
- def initialize( document_path=nil, options={} )
27
+ def initialize(document_path=nil, options={})
49
28
  @document_path = document_path
50
29
  @options = options.clone
51
30
 
52
- if @document_path && File.exists?( document_path )
53
- document_archive = IO.read( document_path )
54
- decoded_document = Codecs::OpenDocument12.new.decode_archive( document_archive, options )
31
+ if @document_path && File.exist?(document_path)
32
+ document_archive = IO.read(document_path)
33
+ decoded_document = Codecs::OpenDocument12.new.decode_archive(document_archive, options)
55
34
 
56
35
  @column_width_styles = decoded_document.column_width_styles
57
36
  @tables = decoded_document.tables
@@ -69,28 +48,28 @@ module SpreadBase # :nodoc:
69
48
  #
70
49
  # +prettify+:: Prettifies the content.xml file before saving.
71
50
  #
72
- def save( options={} )
73
- options = @options.merge( options )
51
+ def save(options={})
52
+ options = @options.merge(options)
74
53
 
75
54
  raise "At least one table must be present" if @tables.empty?
76
55
  raise "Document path not specified" if @document_path.nil?
77
56
 
78
- document_archive = Codecs::OpenDocument12.new.encode_to_archive( self, options )
57
+ document_archive = Codecs::OpenDocument12.new.encode_to_archive(self, options)
79
58
 
80
- File.open( @document_path, 'wb' ) { | file | file << document_archive }
59
+ File.open(@document_path, 'wb') { | file | file << document_archive }
81
60
  end
82
61
 
83
62
  # _options_:
84
63
  #
85
64
  # +with_headers+:: Print the tables with headers.
86
65
  #
87
- def to_s( options={} )
88
- options.merge!( :row_prefix => ' ' )
66
+ def to_s(options={})
67
+ options.merge!(row_prefix: ' ')
89
68
 
90
- tables.inject( '' ) do | output, table |
69
+ tables.inject('') do | output, table |
91
70
  output << "#{ table.name }:" << "\n" << "\n"
92
71
 
93
- output << table.to_s( options ) << "\n"
72
+ output << table.to_s(options) << "\n"
94
73
  end
95
74
  end
96
75
 
@@ -1,23 +1,3 @@
1
- # encoding: UTF-8
2
-
3
- =begin
4
- Copyright 2012 Saverio Miroddi saverio.pub2 <a-hat!> gmail.com
5
-
6
- This file is part of SpreadBase.
7
-
8
- SpreadBase is free software: you can redistribute it and/or modify it under the
9
- terms of the GNU Lesser General Public License as published by the Free Software
10
- Foundation, either version 3 of the License, or (at your option) any later
11
- version.
12
-
13
- SpreadBase is distributed in the hope that it will be useful, but WITHOUT ANY
14
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15
- PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
16
-
17
- You should have received a copy of the GNU Lesser General Public License along
18
- with SpreadBase. If not, see <http://www.gnu.org/licenses/>.
19
- =end
20
-
21
1
  module SpreadBase # :nodoc:
22
2
 
23
3
  # Currently generic helper class
@@ -28,10 +8,10 @@ module SpreadBase # :nodoc:
28
8
  #
29
9
  # The instance is duplicated Object#clone, when necessary - note that this method is not meant to do a deep copy.
30
10
  #
31
- def make_array_from_repetitions( instance, repetitions )
32
- ( 1 .. repetitions ).inject( [] ) do | cumulative_result, i |
11
+ def make_array_from_repetitions(instance, repetitions)
12
+ (1..repetitions).inject([]) do | cumulative_result, i |
33
13
  case instance
34
- when Fixnum, Float, BigDecimal, Date, Time, TrueClass, FalseClass, NilClass #, DateTime is a Date
14
+ when Integer, Float, BigDecimal, Date, Time, TrueClass, FalseClass, NilClass #, DateTime is a Date
35
15
  cumulative_result << instance
36
16
  when String, Array
37
17
  cumulative_result << instance.clone
@@ -54,54 +34,49 @@ module SpreadBase # :nodoc:
54
34
  # +row_prefix+:: Prefix this string to each row.
55
35
  # +with_header+:: First row will be separated from the remaining ones.
56
36
  #
57
- # +formatting_block+:: If passed, values will be formatted by the block.
58
- # If no block is passed, or it returns nil or :standard, the standard formatting is used.
59
- #
60
- def pretty_print_rows( rows, options={}, &formatting_block )
61
- row_prefix = options[ :row_prefix ] || ''
62
- with_headers = options[ :with_headers ]
37
+ def pretty_print_rows(rows, options={})
38
+ row_prefix = options[:row_prefix] || ''
39
+ with_headers = options[:with_headers]
63
40
 
64
41
  output = ""
65
42
 
66
- formatting_block = lambda { | value | value.to_s } if ! block_given?
67
-
68
43
  if rows.size > 0
69
- max_column_sizes = [ 0 ] * rows.map( &:size ).max
44
+ max_column_sizes = [0] * rows.map(&:size).max
70
45
 
71
46
  # Compute maximum widths
72
47
 
73
48
  rows.each do | values |
74
49
  values.each_with_index do | value, i |
75
- formatted_value = pretty_print_value( value, &formatting_block )
50
+ formatted_value = pretty_print_value(value)
76
51
  formatted_value_width = formatted_value.chars.to_a.size
77
52
 
78
- max_column_sizes[ i ] = formatted_value_width if formatted_value_width > max_column_sizes[ i ]
53
+ max_column_sizes[i] = formatted_value_width if formatted_value_width > max_column_sizes[i]
79
54
  end
80
55
  end
81
56
 
82
57
  # Print!
83
58
 
84
- output << row_prefix << '+-' + max_column_sizes.map { | size | '-' * size }.join( '-+-' ) + '-+' << "\n"
59
+ output << row_prefix << '+-' + max_column_sizes.map { | size | '-' * size }.join('-+-') + '-+' << "\n"
85
60
 
86
- print_pattern = '| ' + max_column_sizes.map { | size | "%-#{ size }s" }.join( ' | ' ) + ' |'
61
+ print_pattern = '| ' + max_column_sizes.map { | size | "%-#{ size }s" }.join(' | ') + ' |'
87
62
 
88
63
  rows.each_with_index do | row, row_index |
89
64
  # Ensure that we always have a number of values equal to the max width
90
65
  #
91
- formatted_row_values = ( 0 ... max_column_sizes.size ).map do | column_index |
92
- value = row[ column_index ]
66
+ formatted_row_values = (0...max_column_sizes.size).map do | column_index |
67
+ value = row[column_index]
93
68
 
94
- pretty_print_value( value, &formatting_block )
69
+ pretty_print_value(value)
95
70
  end
96
71
 
97
72
  output << row_prefix << print_pattern % formatted_row_values << "\n"
98
73
 
99
74
  if with_headers && row_index == 0
100
- output << row_prefix << '+-' + max_column_sizes.map { | size | '-' * size }.join( '-+-' ) + '-+' << "\n"
75
+ output << row_prefix << '+-' + max_column_sizes.map { | size | '-' * size }.join('-+-') + '-+' << "\n"
101
76
  end
102
77
  end
103
78
 
104
- output << row_prefix << '+-' + max_column_sizes.map { | size | '-' * size }.join( '-+-' ) + '-+' << "\n"
79
+ output << row_prefix << '+-' + max_column_sizes.map { | size | '-' * size }.join('-+-') + '-+' << "\n"
105
80
  end
106
81
 
107
82
  output
@@ -109,27 +84,18 @@ module SpreadBase # :nodoc:
109
84
 
110
85
  private
111
86
 
112
- def pretty_print_value( value, &formatting_block )
113
- custom_result = block_given? && yield( value )
114
-
115
- if custom_result && custom_result != :standard
116
- custom_result
87
+ def pretty_print_value(value)
88
+ case value
89
+ when BigDecimal
90
+ value.to_s('F')
91
+ when Time, DateTime
92
+ value.strftime('%Y-%m-%d %H:%M:%S %z')
93
+ when String, Date, Numeric, TrueClass, FalseClass
94
+ value.to_s
95
+ when nil
96
+ "NIL"
117
97
  else
118
- case value
119
- when BigDecimal
120
- value.to_s( 'F' )
121
- when Time, DateTime
122
- # Time#to_s renders differently between 1.8.7 and 1.9.3; 1.8.7's rendering is bizarrely
123
- # inconsistent with the Date and DateTime ones.
124
- #
125
- value.strftime( '%Y-%m-%d %H:%M:%S %z' )
126
- when String, Date, Numeric, TrueClass, FalseClass
127
- value.to_s
128
- when nil
129
- "NIL"
130
- else
131
- value.inspect
132
- end
98
+ value.inspect
133
99
  end
134
100
  end
135
101
 
@@ -1,23 +1,3 @@
1
- # encoding: UTF-8
2
-
3
- =begin
4
- Copyright 2012 Saverio Miroddi saverio.pub2 <a-hat!> gmail.com
5
-
6
- This file is part of SpreadBase.
7
-
8
- SpreadBase is free software: you can redistribute it and/or modify it under the
9
- terms of the GNU Lesser General Public License as published by the Free Software
10
- Foundation, either version 3 of the License, or (at your option) any later
11
- version.
12
-
13
- SpreadBase is distributed in the hope that it will be useful, but WITHOUT ANY
14
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15
- PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
16
-
17
- You should have received a copy of the GNU Lesser General Public License along
18
- with SpreadBase. If not, see <http://www.gnu.org/licenses/>.
19
- =end
20
-
21
1
  module SpreadBase # :nodoc:
22
2
 
23
3
  # Represents the abstraction of a table and its contents.
@@ -32,7 +12,7 @@ module SpreadBase # :nodoc:
32
12
 
33
13
  include SpreadBase::Helpers
34
14
 
35
- attr_accessor :name, :data
15
+ attr_accessor :name
36
16
 
37
17
  # Array of style names; nil when not associated to any column width.
38
18
  #
@@ -41,16 +21,24 @@ module SpreadBase # :nodoc:
41
21
  # _params_:
42
22
  #
43
23
  # +name+:: (required) Name of the table
44
- # +data+:: (Array.new) 2d matrix of the data. if not empty, the rows need to be all of the same size
24
+ # +raw_data+:: (Array.new) 2d matrix of the data. if not empty, the rows need to be all of the same size
45
25
  #
46
- def initialize( name, data=[] )
26
+ def initialize(name, raw_data=[])
47
27
  raise "Table name required" if name.nil? || name == ''
48
28
 
49
29
  @name = name
50
- @data = data
30
+ self.data = raw_data
51
31
  @column_width_styles = []
52
32
  end
53
33
 
34
+ def data=(the_data)
35
+ @data = the_data.map { | the_row | array_to_cells(the_row) }
36
+ end
37
+
38
+ def data(options={})
39
+ @data.map { | the_row | the_row.map { | cell | cell_to_value(cell, options) } }
40
+ end
41
+
54
42
  # Access a cell value.
55
43
  #
56
44
  # _params_:
@@ -60,13 +48,14 @@ module SpreadBase # :nodoc:
60
48
  #
61
49
  # _returns_ the value, which is automatically converted to the Ruby data type.
62
50
  #
63
- def []( column_identifier, row_index )
64
- row = row( row_index )
65
- column_index = decode_column_identifier( column_identifier )
51
+ def [](column_identifier, row_index, options={})
52
+ the_row = row(row_index, options)
66
53
 
67
- check_column_index( row, column_index )
54
+ column_index = decode_column_identifier(column_identifier)
68
55
 
69
- row[ column_index ]
56
+ check_column_index(the_row, column_index)
57
+
58
+ the_row[column_index]
70
59
  end
71
60
 
72
61
  # Writes a value in a cell.
@@ -77,25 +66,31 @@ module SpreadBase # :nodoc:
77
66
  # +row_index+:: int (0-based). see notes about the rows indexing.
78
67
  # +value+:: value
79
68
  #
80
- def []=( column_identifier, row_index, value )
81
- row = row( row_index )
82
- column_index = decode_column_identifier( column_identifier )
69
+ def []=(column_identifier, row_index, value)
70
+ check_row_index(row_index)
71
+
72
+ the_row = @data[row_index]
73
+ column_index = decode_column_identifier(column_identifier)
83
74
 
84
- check_column_index( row, column_index )
75
+ check_column_index(the_row, column_index)
85
76
 
86
- row[ column_index ] = value
77
+ the_row[column_index] = value_to_cell(value)
87
78
  end
88
79
 
89
80
  # Returns an array containing the values of a single row.
90
81
  #
91
82
  # _params_:
92
83
  #
93
- # +row_index+:: int (0-based). see notes about the rows indexing.
84
+ # +row_index+:: int or range (0-based). see notes about the rows indexing.
94
85
  #
95
- def row( row_index )
96
- check_row_index( row_index )
86
+ def row(row_index, options={})
87
+ check_row_index(row_index)
97
88
 
98
- @data[ row_index ]
89
+ if row_index.is_a?(Range)
90
+ @data[row_index].map { | row | cells_to_array(row, options) }
91
+ else
92
+ cells_to_array(@data[row_index], options)
93
+ end
99
94
  end
100
95
 
101
96
  # Deletes a row.
@@ -104,14 +99,20 @@ module SpreadBase # :nodoc:
104
99
  #
105
100
  # _params_:
106
101
  #
107
- # +row_index+:: int (0-based). see notes about the rows indexing.
102
+ # +row_index+:: int or range (0-based). see notes about the rows indexing.
108
103
  #
109
- # _returns_ the deleted row
104
+ # _returns_ the deleted row[s]
110
105
  #
111
- def delete_row( row_index )
112
- check_row_index( row_index )
106
+ def delete_row(row_index)
107
+ check_row_index(row_index)
113
108
 
114
- @data.slice!( row_index )
109
+ deleted_cells = @data.slice!(row_index)
110
+
111
+ if row_index.is_a?(Range)
112
+ deleted_cells.map { | row | cells_to_array(row) }
113
+ else
114
+ cells_to_array(deleted_cells)
115
+ end
115
116
  end
116
117
 
117
118
  # Inserts a row.
@@ -123,16 +124,18 @@ module SpreadBase # :nodoc:
123
124
  # +row_index+:: int (0-based). must be between 0 and (including) the table rows size.
124
125
  # +row+:: array of values. if the table is not empty, must have the same size of the table width.
125
126
  #
126
- def insert_row( row_index, row )
127
- check_row_index( row_index, :allow_append => true )
127
+ def insert_row(row_index, row)
128
+ check_row_index(row_index, allow_append: true)
128
129
 
129
- @data.insert( row_index, row )
130
+ cells = array_to_cells(row)
131
+
132
+ @data.insert(row_index, cells)
130
133
  end
131
134
 
132
135
  # This operation won't modify the column width styles in any case.
133
136
  #
134
- def append_row( row )
135
- insert_row( @data.size, row )
137
+ def append_row(row)
138
+ insert_row(@data.size, row)
136
139
  end
137
140
 
138
141
  # Returns an array containing the values of a single column.
@@ -141,35 +144,68 @@ module SpreadBase # :nodoc:
141
144
  #
142
145
  # _params_:
143
146
  #
144
- # +column_indentifier+:: either an int (0-based) or the excel-format identifier (AA...).
147
+ # +column_indentifier+:: for single access, us either an int (0-based) or the excel-format identifier (AA...).
145
148
  # when int, follow the same idea of the rows indexing (ruby semantics).
149
+ # for multiple access, use a range either of int or excel-format identifiers - pay attention, because ( 'A'..'c' ) is not semantically correct.
150
+ # interestingly, ruby letter ranges convention is the same as the excel columns one.
146
151
  #
147
- def column( column_identifier )
148
- column_index = decode_column_identifier( column_identifier )
152
+ def column(column_identifier, options={})
153
+ if column_identifier.is_a?(Range)
154
+ min_index = decode_column_identifier(column_identifier.min)
155
+ max_index = decode_column_identifier(column_identifier.max)
156
+
157
+ (min_index..max_index).map do | column_index |
158
+ @data.map do | the_row |
159
+ cell = the_row[column_index]
160
+
161
+ cell_to_value(cell, options)
162
+ end
163
+ end
164
+ else
165
+ column_index = decode_column_identifier(column_identifier)
149
166
 
150
- @data.map do | row |
151
- row[ column_index ]
167
+ @data.map do | the_row |
168
+ cell = the_row[column_index]
169
+
170
+ cell_to_value(cell, options)
171
+ end
152
172
  end
153
173
  end
154
174
 
155
175
  # Deletes a column.
156
176
  #
157
- # WATCH OUT! This method doesn't have the range restrictions that axis indexes generally has, that is, it's possible to delete a column outside the boundaries of the rows - it will return nil for each of those values.
177
+ # See Table#column for the indexing notes.
158
178
  #
159
179
  # _params_:
160
180
  #
161
- # +column_indentifier+:: either an int (0-based) or the excel-format identifier (AA...).
162
- # when int, follow the same idea of the rows indexing (ruby semantics).
181
+ # +column_indentifier+:: See Table#column
163
182
  #
164
183
  # _returns_ the deleted column
165
184
  #
166
- def delete_column( column_identifier )
167
- column_index = decode_column_identifier( column_identifier )
185
+ def delete_column(column_identifier)
186
+ if column_identifier.is_a?(Range)
187
+ min_index = decode_column_identifier(column_identifier.min)
188
+ max_index = decode_column_identifier(column_identifier.max)
168
189
 
169
- @column_width_styles.slice!( column_index )
190
+ reverse_result = max_index.downto(min_index).map do | column_index |
191
+ @data.map do | row |
192
+ cell = row.slice!(column_index)
170
193
 
171
- @data.map do | row |
172
- row.slice!( column_index )
194
+ cell_to_value(cell)
195
+ end
196
+ end
197
+
198
+ reverse_result.reverse
199
+ else
200
+ column_index = decode_column_identifier(column_identifier)
201
+
202
+ @column_width_styles.slice!(column_index)
203
+
204
+ @data.map do | row |
205
+ cell = row.slice!(column_index)
206
+
207
+ cell_to_value(cell)
208
+ end
173
209
  end
174
210
  end
175
211
 
@@ -183,37 +219,63 @@ module SpreadBase # :nodoc:
183
219
  # when int, follow the same idea of the rows indexing (ruby semantics).
184
220
  # +column+:: array of values. if the table is not empty, it must have the same size of the table height.
185
221
  #
186
- def insert_column( column_identifier, column )
222
+ def insert_column(column_identifier, column)
187
223
  raise "Inserting column size (#{ column.size }) different than existing columns size (#{ @data.size })" if @data.size > 0 && column.size != @data.size
188
224
 
189
- column_index = decode_column_identifier( column_identifier )
225
+ column_index = decode_column_identifier(column_identifier)
190
226
 
191
- @column_width_styles.insert( column_index, nil )
227
+ @column_width_styles.insert(column_index, nil)
192
228
 
193
229
  if @data.size > 0
194
- @data.zip( column ).each do | row, value |
195
- row.insert( column_index, value )
230
+ @data.zip(column).each do | row, value |
231
+ cell = value_to_cell(value)
232
+
233
+ row.insert(column_index, cell)
196
234
  end
197
235
  else
198
- @data = column.map { | value | [ value ] }
236
+ @data = column.map do | value |
237
+ [value_to_cell(value)]
238
+ end
199
239
  end
200
240
 
201
241
  end
202
242
 
203
- def append_column( column )
243
+ def append_column(column)
204
244
  column_index = @data.size > 0 ? @data.first.size : 0
205
245
 
206
- insert_column( column_index, column )
246
+ insert_column(column_index, column)
207
247
  end
208
248
 
209
249
  # _returns_ a matrix representation of the tables, with the values being separated by commas.
210
250
  #
211
- def to_s( options={} )
212
- pretty_print_rows( @data, options )
251
+ def to_s(options={})
252
+ pretty_print_rows(data, options)
213
253
  end
214
254
 
215
255
  private
216
256
 
257
+ def array_to_cells(the_row)
258
+ the_row.map { | value | value_to_cell(value) }
259
+ end
260
+
261
+ def value_to_cell(value)
262
+ value.is_a?(Cell) ? value : Cell.new(value)
263
+ end
264
+
265
+ def cells_to_array(cells, options={})
266
+ cells.map { | cell | cell_to_value(cell, options) }
267
+ end
268
+
269
+ def cell_to_value(cell, options={})
270
+ as_cell = options[:as_cell]
271
+
272
+ if as_cell
273
+ cell
274
+ else
275
+ cell.value if cell
276
+ end
277
+ end
278
+
217
279
  # Check that row index points to an existing record, or, in case of :allow_append,
218
280
  # point to one unit above the last row.
219
281
  #
@@ -221,15 +283,17 @@ module SpreadBase # :nodoc:
221
283
  #
222
284
  # +allow_append+:: Allow pointing to one unit above the last row.
223
285
  #
224
- def check_row_index( row_index, options={} )
225
- allow_append = options [ :allow_append ]
286
+ def check_row_index(row_index, options={})
287
+ allow_append = options [:allow_append]
226
288
 
227
289
  positive_limit = allow_append ? @data.size : @data.size - 1
228
290
 
291
+ row_index = row_index.max if row_index.is_a?(Range)
292
+
229
293
  raise "Invalid row index (#{ row_index }) - allowed 0 to #{ positive_limit }" if row_index < 0 || row_index > positive_limit
230
294
  end
231
295
 
232
- def check_column_index( row, column_index )
296
+ def check_column_index(row, column_index)
233
297
  raise "Invalid column index (#{ column_index }) for the given row - allowed 0 to #{ row.size - 1 }" if column_index >= row.size
234
298
  end
235
299
 
@@ -238,12 +302,9 @@ module SpreadBase # :nodoc:
238
302
  # Raises an error for invalid identifiers/indexes.
239
303
  #
240
304
  # _returns_ a 0-based decimal number.
241
- #--
242
- # Motherf#### base-26 bijective numeration - I would have gladly saved my f* time. At least
243
- # there were a few cute ladies at the Charleston lesson.
244
305
  #
245
- def decode_column_identifier( column_identifier )
246
- if column_identifier.is_a?( Fixnum )
306
+ def decode_column_identifier(column_identifier)
307
+ if column_identifier.is_a?(Integer)
247
308
  raise "Negative column indexes not allowed: #{ column_identifier }" if column_identifier < 0
248
309
 
249
310
  column_identifier
@@ -253,9 +314,9 @@ module SpreadBase # :nodoc:
253
314
 
254
315
  raise "Invalid letter for in column identifier (allowed 'a/A' to 'z/Z')" if letters.any? { | letter | letter < 'A' || letter > 'Z' }
255
316
 
256
- base_10_value = letters.inject( 0 ) do | sum, letter |
257
- letter_ord = letter.unpack( 'C' ).first
258
- sum * 26 + ( letter_ord - upcase_a_ord + 1 )
317
+ base_10_value = letters.inject(0) do | sum, letter |
318
+ letter_ord = letter.unpack('C').first
319
+ sum * 26 + (letter_ord - upcase_a_ord + 1)
259
320
  end
260
321
 
261
322
  base_10_value -= 1