rspreadsheet 0.2.12 → 0.2.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -1
- data/DEVEL_BLOG.md +4 -0
- data/GUIDE.md +6 -5
- data/README.md +6 -6
- data/lib/helpers/class_extensions.rb +95 -39
- data/lib/rspreadsheet/cell.rb +5 -3
- data/lib/rspreadsheet/column.rb +17 -0
- data/lib/rspreadsheet/row.rb +3 -351
- data/lib/rspreadsheet/version.rb +1 -1
- data/lib/rspreadsheet/worksheet.rb +9 -1
- data/lib/rspreadsheet/xml_tied.rb +6 -2
- data/spec/cell_spec.rb +83 -85
- data/spec/class_extensions_spec.rb +46 -0
- data/spec/column_spec.rb +18 -0
- data/spec/rspreadsheet_spec.rb +3 -2
- data/spec/worksheet_spec.rb +1 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d2f168c71e8ec1b014d48658b230c03780c6fb40
|
4
|
+
data.tar.gz: 11aaf6ed2a306be9eb3a2f3dcfa89dffed291f88
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fa517af98e5a410bd50f9bb62a878b8752bed69bc77eb1cff3d50dcb05839c4a81956417a64a8b3f32b55376c1906d3c983c3260e426ccdc69145768063e6639
|
7
|
+
data.tar.gz: 95e37cd4a06b66f107b612c8467ca91f2524bf61477208129e159dc0a479a476bc9be5e965eefd1b573c70919b2fd8cb28ce33d0eb5c299064cbef044bbe5a56
|
data/.travis.yml
CHANGED
data/DEVEL_BLOG.md
CHANGED
@@ -49,6 +49,10 @@ RSpreadsheet.generate('pricelist.ods') do
|
|
49
49
|
* Range#merge() - Merges the cells in the range together into a single block.
|
50
50
|
* @book.sheet_names - Array of names of sheets.
|
51
51
|
|
52
|
+
|
53
|
+
* array returned by rows of cells can have predefined dummy object as defaults for out of range indexes
|
54
|
+
|
55
|
+
|
52
56
|
##Guiding ideas
|
53
57
|
* xml document is always synchronized with the data. So the save is trivial.
|
54
58
|
* no duplication of data. Objects like RowArray should containg minimum information. This one exists solely to speed up cell search. Taken to extream it is questionable, whether we need such objects at all, it might be possible to always work with xml directly.
|
data/GUIDE.md
CHANGED
@@ -7,14 +7,14 @@ You can open ODS file like this
|
|
7
7
|
````
|
8
8
|
and access its first sheet like this
|
9
9
|
````ruby
|
10
|
-
@sheet = @workbook.
|
10
|
+
@sheet = @workbook.worksheet(1)
|
11
11
|
````
|
12
12
|
### Accessing cells
|
13
13
|
|
14
14
|
you can get and set contents of cells using "verbatim" syntax like
|
15
15
|
````ruby
|
16
|
-
@sheet.
|
17
|
-
@sheet.
|
16
|
+
@sheet.row(5).cell(4).value
|
17
|
+
@sheet.row(5).cell(4).value = 10
|
18
18
|
````
|
19
19
|
or using "brief" syntax like
|
20
20
|
````ruby
|
@@ -24,7 +24,7 @@ or using "brief" syntax like
|
|
24
24
|
|
25
25
|
You can mix these two at will, for example like this
|
26
26
|
````ruby
|
27
|
-
@row = @sheet.
|
27
|
+
@row = @sheet.row(5)
|
28
28
|
@row[4] = 10
|
29
29
|
````
|
30
30
|
|
@@ -37,5 +37,6 @@ You can mix these two at will, for example like this
|
|
37
37
|
* **all indexes are 1-based**. This applies to rows, cells cordinates, and all array like structures like list od worksheets etc. Spreadsheet world is 1-based, ruby is 0-based do I had to make a decision. I intend to make an global option for this, but in early stage I need to keep things simple.
|
38
38
|
* with numeric coordinates row always comes before col as in (row,col)
|
39
39
|
* with alphanumerical col always comes before row as in F12
|
40
|
-
* Shorter syntax worksheet[x,y] returns value, longer syntax worksheet.
|
40
|
+
* Shorter syntax worksheet[x,y] returns value, longer syntax worksheet.cell(x,y) return cell objects. This allows to work conviniently with values using short syntax and access the cell object if needed (to access formatting for example).
|
41
|
+
* Note: currently plural and singular like sheet/sheets, row/rows, cell/cells can be used intergangebly, but there is a (discussion)[https://github.com/gorn/rspreadsheet/issues/10] about this and in future versions we might use singular style for methods and plural for array style.
|
41
42
|
|
data/README.md
CHANGED
@@ -16,20 +16,20 @@ sheet = book.worksheets(1)
|
|
16
16
|
# get value of a cell B5 (there are more ways to do this)
|
17
17
|
sheet.B5 # => 'cell value'
|
18
18
|
sheet[5,2] # => 'cell value'
|
19
|
-
sheet.
|
19
|
+
sheet.row(5).cell(2).value # => 'cell value'
|
20
20
|
|
21
21
|
# set value of a cell B5
|
22
22
|
sheet.F5 = 'text'
|
23
23
|
sheet[5,2] = 7
|
24
|
-
sheet.
|
24
|
+
sheet.cell(5,2).value = 1.78
|
25
25
|
|
26
26
|
# working with cell format
|
27
|
-
sheet.
|
28
|
-
sheet.
|
27
|
+
sheet.cell(5,2).format.bold = true
|
28
|
+
sheet.cell(5,2).format.background_color = '#FF0000'
|
29
29
|
|
30
30
|
# calculate sum of cells in row
|
31
|
-
sheet.
|
32
|
-
sheet.
|
31
|
+
sheet.row(5).cellvalues.sum
|
32
|
+
sheet.row(5).cells.sum{ |cell| cell.value.to_f }
|
33
33
|
|
34
34
|
# or set formula to a cell
|
35
35
|
sheet.A1.formula='=SUM(A2:A9)'
|
@@ -1,49 +1,105 @@
|
|
1
|
-
|
1
|
+
if RUBY_VERSION > '2.1'
|
2
|
+
|
3
|
+
module ClassExtensions
|
4
|
+
|
5
|
+
refine Array do
|
6
|
+
def sum(identity = 0, &block)
|
7
|
+
if block_given?
|
8
|
+
map(&block).sum(identity)
|
9
|
+
else
|
10
|
+
inject(0){ |sum, element| sum.to_f + element.to_f } || identity
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
refine LibXML::XML::Node do
|
16
|
+
def ==(node2)
|
17
|
+
self.simplification_of?(node2) and node2.simplification_of?(self)
|
18
|
+
end
|
19
|
+
# if node2 contains at least all that I do
|
20
|
+
def simplification_of?(node2)
|
21
|
+
first_diff(node2).nil?
|
22
|
+
end
|
23
|
+
# return first difference where self has something more than node2 does
|
24
|
+
def first_diff(node2)
|
25
|
+
where = self.path.split('/').last
|
26
|
+
|
27
|
+
return "#{where}> Equivalent node does not exist: #{self.name} != NOTHING" if node2.nil?
|
28
|
+
return "#{where}> Names are different: #{self.name} != #{node2.name}" if (self.name != node2.name)
|
29
|
+
self.attributes.each do |attr|
|
30
|
+
return "#{where}> Attribute #{attr} have diffent values: #{attr.value} != #{node2.attributes[attr.name]}" unless node2.attributes[attr.name] == attr.value
|
31
|
+
end
|
32
|
+
|
33
|
+
elems1 = self.elements
|
34
|
+
elems2 = node2.elements
|
35
|
+
# return "#{where}> elements have different number of subelements #{elems1.length} != #{elems2.length}" if (elems1.length != elems2.length)
|
36
|
+
elems1.length.times do |i|
|
37
|
+
if (elems1[i].node_type_name == 'text') && ((elems1[i].to_s != elems2[i].to_s) )
|
38
|
+
return "#{where}> #{i+1}th text subelements are different: #{elems1[i].to_s} != #{elems2[i].to_s}"
|
39
|
+
elsif (elems1[i].node_type_name == 'element') && (!elems1[i].simplification_of?(elems2[i]))
|
40
|
+
return "#{where}/[#{i+1}]#{elems1[i].first_diff(elems2[i])}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
return nil
|
45
|
+
end
|
46
|
+
def elements
|
47
|
+
result = []
|
48
|
+
each_element { |e| result << e }
|
49
|
+
return result
|
50
|
+
end
|
2
51
|
|
3
|
-
class LibXML::XML::Node
|
4
|
-
def elements
|
5
|
-
result = []
|
6
|
-
each_element { |e| result << e }
|
7
|
-
return result
|
8
|
-
end
|
9
|
-
# if node2 contains at least all that I do
|
10
|
-
def simplification_of?(node2)
|
11
|
-
first_diff(node2).nil?
|
12
|
-
end
|
13
|
-
# return first difference where self has something more than node2 does
|
14
|
-
def first_diff(node2)
|
15
|
-
where = self.path.split('/').last
|
16
|
-
|
17
|
-
return "#{where}> Equivalent node does not exist: #{self.name} != NOTHING" if node2.nil?
|
18
|
-
return "#{where}> Names are different: #{self.name} != #{node2.name}" if (self.name != node2.name)
|
19
|
-
self.attributes.each do |attr|
|
20
|
-
return "#{where}> Attribute #{attr} have diffent values: #{attr.value} != #{node2.attributes[attr.name]}" unless node2.attributes[attr.name] == attr.value
|
21
52
|
end
|
22
53
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
54
|
+
end # module ClassExtensions
|
55
|
+
|
56
|
+
else # Monkeypatching
|
57
|
+
class Array
|
58
|
+
def sum(identity = 0, &block)
|
59
|
+
if block_given?
|
60
|
+
map(&block).sum(identity)
|
61
|
+
else
|
62
|
+
inject(0){ |sum, element| sum.to_f + element.to_f } || identity
|
31
63
|
end
|
32
64
|
end
|
33
|
-
|
34
|
-
return nil
|
35
65
|
end
|
36
|
-
def equals?(node2) #TODO redefine == with this
|
37
|
-
self.simplification_of?(node2) and node2.simplification_of?(self)
|
38
|
-
end
|
39
|
-
end
|
40
66
|
|
41
|
-
class
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
67
|
+
class LibXML::XML::Node
|
68
|
+
def ==(node2)
|
69
|
+
self.simplification_of?(node2) and node2.simplification_of?(self)
|
70
|
+
end
|
71
|
+
# if node2 contains at least all that I do
|
72
|
+
def simplification_of?(node2)
|
73
|
+
first_diff(node2).nil?
|
47
74
|
end
|
75
|
+
# return first difference where self has something more than node2 does
|
76
|
+
def first_diff(node2)
|
77
|
+
where = self.path.split('/').last
|
78
|
+
|
79
|
+
return "#{where}> Equivalent node does not exist: #{self.name} != NOTHING" if node2.nil?
|
80
|
+
return "#{where}> Names are different: #{self.name} != #{node2.name}" if (self.name != node2.name)
|
81
|
+
self.attributes.each do |attr|
|
82
|
+
return "#{where}> Attribute #{attr} have diffent values: #{attr.value} != #{node2.attributes[attr.name]}" unless node2.attributes[attr.name] == attr.value
|
83
|
+
end
|
84
|
+
|
85
|
+
elems1 = self.elements
|
86
|
+
elems2 = node2.elements
|
87
|
+
# return "#{where}> elements have different number of subelements #{elems1.length} != #{elems2.length}" if (elems1.length != elems2.length)
|
88
|
+
elems1.length.times do |i|
|
89
|
+
if (elems1[i].node_type_name == 'text') && ((elems1[i].to_s != elems2[i].to_s) )
|
90
|
+
return "#{where}> #{i+1}th text subelements are different: #{elems1[i].to_s} != #{elems2[i].to_s}"
|
91
|
+
elsif (elems1[i].node_type_name == 'element') && (!elems1[i].simplification_of?(elems2[i]))
|
92
|
+
return "#{where}/[#{i+1}]#{elems1[i].first_diff(elems2[i])}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
return nil
|
97
|
+
end
|
98
|
+
def elements
|
99
|
+
result = []
|
100
|
+
each_element { |e| result << e }
|
101
|
+
return result
|
102
|
+
end
|
103
|
+
|
48
104
|
end
|
49
105
|
end
|
data/lib/rspreadsheet/cell.rb
CHANGED
@@ -7,8 +7,10 @@ require 'rspreadsheet/xml_tied'
|
|
7
7
|
require 'date'
|
8
8
|
require 'bigdecimal'
|
9
9
|
require 'bigdecimal/util' # for to_d method
|
10
|
+
require 'helpers/class_extensions'
|
10
11
|
|
11
12
|
module Rspreadsheet
|
13
|
+
using ClassExtensions if RUBY_VERSION > '2.1'
|
12
14
|
|
13
15
|
###
|
14
16
|
# Represents a cell in spreadsheet which has coordinates, contains value, formula and can be formated.
|
@@ -40,7 +42,7 @@ class Cell < XMLTiedItem
|
|
40
42
|
def coordinates; [rowi,coli] end
|
41
43
|
def to_s; value.to_s end
|
42
44
|
def valuexml; self.valuexmlnode.andand.inner_xml end
|
43
|
-
def valuexmlnode; self.xmlnode.
|
45
|
+
def valuexmlnode; self.xmlnode.elements.first end
|
44
46
|
# use this to find node in cell xml. ex. xmlfind('.//text:a') finds all link nodes
|
45
47
|
def valuexmlfindall(path)
|
46
48
|
valuexmlnode.nil? ? [] : valuexmlnode.find(path)
|
@@ -150,10 +152,10 @@ class Cell < XMLTiedItem
|
|
150
152
|
when 'N/A' then :unassigned
|
151
153
|
when 'currency' then :currency
|
152
154
|
else
|
153
|
-
if xmlnode.
|
155
|
+
if xmlnode.elements.size == 0
|
154
156
|
nil
|
155
157
|
else
|
156
|
-
raise "Unknown type at #{coordinates.to_s} from #{xmlnode.to_s} /
|
158
|
+
raise "Unknown type at #{coordinates.to_s} from #{xmlnode.to_s} / elements size=#{xmlnode.elements.size.to_s} / type=#{xmlnode.attributes['value-type'].to_s}"
|
157
159
|
end
|
158
160
|
end
|
159
161
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Rspreadsheet
|
2
|
+
|
3
|
+
# Represents a columns in a spreadsheet. Similar to Row object, but
|
4
|
+
# currently only partly implemented
|
5
|
+
|
6
|
+
class Column
|
7
|
+
def initialize(aworksheet,acoli)
|
8
|
+
@worksheet = aworksheet
|
9
|
+
@coli = acoli
|
10
|
+
end
|
11
|
+
def cell(rowi)
|
12
|
+
@worksheet.row(rowi).cell(@coli)
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
data/lib/rspreadsheet/row.rb
CHANGED
@@ -7,7 +7,7 @@ module Rspreadsheet
|
|
7
7
|
# Represents a row in a spreadsheet which has coordinates, contains value, formula and can be formated.
|
8
8
|
# You can get this object like this (suppose that @worksheet contains {Rspreadsheet::Worksheet} object)
|
9
9
|
#
|
10
|
-
# @row = @worksheet.
|
10
|
+
# @row = @worksheet.row(5)
|
11
11
|
#
|
12
12
|
# Mostly you will this object to access row cells values
|
13
13
|
#
|
@@ -15,7 +15,7 @@ module Rspreadsheet
|
|
15
15
|
#
|
16
16
|
# or directly row `Cell` objects
|
17
17
|
#
|
18
|
-
# @row.
|
18
|
+
# @row.cell(2) # => identical to @worksheet.rows(5).cells(2)
|
19
19
|
#
|
20
20
|
# You can use it to manipulate rows
|
21
21
|
#
|
@@ -43,6 +43,7 @@ class Row < XMLTiedItem
|
|
43
43
|
|
44
44
|
# @!group Syntactic sugar
|
45
45
|
def cells(*params); subitems(*params) end
|
46
|
+
alias :cell :cells
|
46
47
|
|
47
48
|
## @return [String or Float or Date] value of the cell
|
48
49
|
# @param coli [Integer] colum index of the cell
|
@@ -119,353 +120,4 @@ class Row < XMLTiedItem
|
|
119
120
|
def set_index(value); @rowi=value end
|
120
121
|
end
|
121
122
|
|
122
|
-
# class Row
|
123
|
-
# def initialize
|
124
|
-
# @readonly = :unknown
|
125
|
-
# @cells = {}
|
126
|
-
# end
|
127
|
-
# def worksheet; @parent_array.worksheet end
|
128
|
-
# def parent_array; @parent_array end # for debug only
|
129
|
-
# def used_col_range; 1..first_unused_column_index-1 end
|
130
|
-
# def used_range; used_col_range end
|
131
|
-
# def first_unused_column_index; raise 'this should be redefined in subclasses' end
|
132
|
-
# end
|
133
|
-
|
134
|
-
|
135
|
-
# --------------------------
|
136
|
-
|
137
|
-
|
138
|
-
# # XmlTiedArrayItemGroup is internal representation of repeated items in XmlTiedArray.
|
139
|
-
# class XmlTiedArrayItemGroup
|
140
|
-
# # extend Forwardable
|
141
|
-
# # delegate [:normalize ] => :@row_group
|
142
|
-
#
|
143
|
-
# def normalize; @rowgroup.normalize end
|
144
|
-
|
145
|
-
# end
|
146
|
-
|
147
|
-
# array which synchronizes with xml structure and reflects. number-xxx-repeated attributes
|
148
|
-
# also caches returned objects for indexes.
|
149
|
-
# options must contain
|
150
|
-
# :xml_items, :xml_repeated_attribute, :object_type
|
151
|
-
|
152
|
-
# class XmlTiedArray < Array
|
153
|
-
# def initialize(axmlnode, options={}) # TODO get rid of XmlTiedArray
|
154
|
-
# @xmlnode = axmlnode
|
155
|
-
# @options = options
|
156
|
-
#
|
157
|
-
# missing_options = [:xml_repeated_attribute,:xml_items_node_name,:object_type]-@options.keys
|
158
|
-
# raise "Some options missing (#{missing_options.inspect})" unless missing_options.empty?
|
159
|
-
#
|
160
|
-
# unless @xmlnode.nil?
|
161
|
-
# @xmlnode.elements.select{|node| node.name == options[:xml_items_node_name]}.each do |group_source_node|
|
162
|
-
# self << parse_xml_to_group(group_source_node) # it is in @xmlnode so suffices to add object to @rowgroups
|
163
|
-
# end
|
164
|
-
# end
|
165
|
-
# @itemcache=Hash.new()
|
166
|
-
# end
|
167
|
-
# def parse_xml_to_group(size_or_xmlnode) # parses xml to new RowGroup which can be added at the end
|
168
|
-
# # reading params
|
169
|
-
# if size_or_xmlnode.kind_of? LibXML::XML::Node
|
170
|
-
# size = (size_or_xmlnode[@options[:xml_repeated_attribute]] || 1).to_i
|
171
|
-
# node = size_or_xmlnode
|
172
|
-
# elsif size_or_xmlnode.to_i>0
|
173
|
-
# size = size_or_xmlnode.to_i
|
174
|
-
# node = nil
|
175
|
-
# else
|
176
|
-
# return nil
|
177
|
-
# end
|
178
|
-
# index = first_unused_index
|
179
|
-
# # construct result
|
180
|
-
# Rspreadsheet::XmlTiedArrayItemGroup.new(self,index..index+size-1,node)
|
181
|
-
# end
|
182
|
-
# def add_item_group(size_or_xmlnode)
|
183
|
-
# result = parse_xml_to_group(size_or_xmlnode)
|
184
|
-
# self << result
|
185
|
-
# @xmlnode << result.xmlnode
|
186
|
-
# result
|
187
|
-
# end
|
188
|
-
# def first_unused_index
|
189
|
-
# empty? ? 1 : last.range.end+1
|
190
|
-
# end
|
191
|
-
# # prolonges the RowArray to cantain rowi and returns it
|
192
|
-
# def detach_of_bound_item(index)
|
193
|
-
# fill_row_group_size = index-first_unused_index
|
194
|
-
# if fill_row_group_size>0
|
195
|
-
# add_item_group(fill_row_group_size)
|
196
|
-
# end
|
197
|
-
# add_item_group(1)
|
198
|
-
# get_item(index) # aby se odpoved nacacheovala
|
199
|
-
# end
|
200
|
-
# def get_item_group(index)
|
201
|
-
# find{ |item_group| item_group.range.cover?(index) }
|
202
|
-
# end
|
203
|
-
# def detach_item(index); get_item(index) end # TODO předělat do lazy podoby, kdy tohle nebude stejny
|
204
|
-
# def get_item(index)
|
205
|
-
# if index>= first_unused_index
|
206
|
-
# nil
|
207
|
-
# else
|
208
|
-
# @itemcache[index] ||= Rspreadsheet::XmlTiedArrayItem.new(self,index)
|
209
|
-
# end
|
210
|
-
# end
|
211
|
-
# # This detaches item index from the group and perhaps splits the RowGroup
|
212
|
-
# # into two pieces. This makes the row individually editable.
|
213
|
-
# def detach(index)
|
214
|
-
# group_index = get_group_index(index)
|
215
|
-
# item_group = self[group_index]
|
216
|
-
# range = item_group.range
|
217
|
-
# return self if range==(index..index)
|
218
|
-
#
|
219
|
-
# # prepare new components
|
220
|
-
# replaceby = []
|
221
|
-
# replaceby << RowGroup.new(self,range.begin..index-1)
|
222
|
-
# replaceby << (result = SingleRow.new(self,index))
|
223
|
-
# replaceby << RowGroup.new(self,index+1..range.end)
|
224
|
-
#
|
225
|
-
# # put original range somewhere in replaceby and shorten it
|
226
|
-
#
|
227
|
-
# if index>range.begin
|
228
|
-
# replaceby[0] = item_group
|
229
|
-
# item_group.range = range.begin..index-1
|
230
|
-
# else
|
231
|
-
# replaceby[2] = item_group
|
232
|
-
# item_group.range = index+1..range.end
|
233
|
-
# end
|
234
|
-
#
|
235
|
-
# # normalize and delete empty parts
|
236
|
-
# replaceby = replaceby.map(&:normalize).compact
|
237
|
-
#
|
238
|
-
# # do the replacement in xml
|
239
|
-
# marker = LibXML::XML::Node.new('temporarymarker')
|
240
|
-
# item_group.xmlnode.next = marker
|
241
|
-
# item_group.xmlnode.remove!
|
242
|
-
# replaceby.each{ |rg|
|
243
|
-
# marker.prev = rg.xmlnode
|
244
|
-
# }
|
245
|
-
# marker.remove!
|
246
|
-
#
|
247
|
-
# # do the replacement in array
|
248
|
-
# self[group_index..group_index]=replaceby
|
249
|
-
# result
|
250
|
-
# end
|
251
|
-
# private
|
252
|
-
# def get_group_index(index)
|
253
|
-
# self.find_index{ |rowgroup| rowgroup.range.cover?(index) }
|
254
|
-
# end
|
255
|
-
# end
|
256
|
-
|
257
|
-
# class XmlTiedArrayItem
|
258
|
-
# attr_reader :index
|
259
|
-
# def initialize(aarray,aindex)
|
260
|
-
# @array = aarray
|
261
|
-
# @index = aindex
|
262
|
-
# if self.virtual?
|
263
|
-
# @object = nil
|
264
|
-
# else
|
265
|
-
# @object = @array.options[:object_type].new(group.xmlnode)
|
266
|
-
# end
|
267
|
-
# end
|
268
|
-
# def group; @array.get_item_group(index) end
|
269
|
-
# def repeated?; group.repeated? end
|
270
|
-
# def virtual?; ! self.repeated? end
|
271
|
-
# def array
|
272
|
-
# raise 'Group empty' if @group.nil?
|
273
|
-
# @array
|
274
|
-
# end
|
275
|
-
# end
|
276
|
-
|
277
|
-
# class RowArray < XmlTiedArray
|
278
|
-
# attr_reader :row_array_cache
|
279
|
-
# def initialize(aworksheet,aworksheet_node)
|
280
|
-
# @worksheet = aworksheet
|
281
|
-
# @row_array_cache = Hash.new()
|
282
|
-
# super(aworksheet_node, :xml_items_node_name => 'table-row', :xml_repeated_attribute => xml_repeated_attribute, :object_type=>Row)
|
283
|
-
# end
|
284
|
-
# def get_row(rowi)
|
285
|
-
# if @row_array_cache.has_key?(rowi)
|
286
|
-
# return @row_array_cache[rowi]
|
287
|
-
# end
|
288
|
-
# item = self.get_item(rowi)
|
289
|
-
# @row_array_cache[rowi] = if item.nil?
|
290
|
-
# if rowi>0 then Rspreadsheet::UninitializedEmptyRow.new(self,rowi) else nil end
|
291
|
-
# else
|
292
|
-
# if item.repeated?
|
293
|
-
# Rspreadsheet::MemberOfRowGroup.new(item.index, item.group.to_rowgroup)
|
294
|
-
# else
|
295
|
-
# Rspreadsheet::SingleRow.new_from_rowgroup(item.group.to_rowgroup)
|
296
|
-
# end
|
297
|
-
# end
|
298
|
-
# end
|
299
|
-
# # aliases
|
300
|
-
# def first_unused_row_index; first_unused_index end
|
301
|
-
# def worksheet; @worksheet end
|
302
|
-
# def detach_of_bound_row_group(index)
|
303
|
-
# super(index)
|
304
|
-
# return get_row(index)
|
305
|
-
# end
|
306
|
-
# end
|
307
|
-
|
308
|
-
# class Row
|
309
|
-
# def initialize
|
310
|
-
# @readonly = :unknown
|
311
|
-
# @cells = {}
|
312
|
-
# end
|
313
|
-
# def self.empty_row_node
|
314
|
-
# LibXML::XML::Node.new('table-row',nil, Tools.get_namespace('table'))
|
315
|
-
# end
|
316
|
-
# def worksheet; @parent_array.worksheet end
|
317
|
-
# def parent_array; @parent_array end # for debug only
|
318
|
-
# def used_col_range; 1..first_unused_column_index-1 end
|
319
|
-
# def used_range; used_col_range end
|
320
|
-
# def first_unused_column_index; raise 'this should be redefined in subclasses' end
|
321
|
-
# def cells(coli)
|
322
|
-
# coli = coli.to_i
|
323
|
-
# return nil if coli.to_i<=0
|
324
|
-
# @cells[coli] ||= get_cell(coli)
|
325
|
-
# end
|
326
|
-
# end
|
327
|
-
|
328
|
-
# class RowWithXMLNode < Row
|
329
|
-
# attr_accessor :xmlnode
|
330
|
-
# def style_name=(value); Tools.set_ns_attribute(@xmlnode,'table','style-name',value) end
|
331
|
-
# def get_cell(coli)
|
332
|
-
# Cell.new(self,coli,cellnodes(coli))
|
333
|
-
# end
|
334
|
-
# def nonemptycells
|
335
|
-
# nonemptycellsindexes.collect{ |index| cells(index) }
|
336
|
-
# end
|
337
|
-
# def nonemptycellsindexes
|
338
|
-
# used_col_range.to_a.select do |coli|
|
339
|
-
# cellnode = cellnodes(coli)
|
340
|
-
# !(cellnode.content.nil? or cellnode.content.empty? or cellnode.content =='') or
|
341
|
-
# !cellnode.attributes.to_a.reject{ |attr| attr.name == 'number-columns-repeated'}.empty?
|
342
|
-
# end
|
343
|
-
# end
|
344
|
-
# def cellnodes(coli)
|
345
|
-
# cellnode = nil
|
346
|
-
# while true
|
347
|
-
# curr_coli=1
|
348
|
-
# cellnode = @xmlnode.elements.select{|n| n.name=='table-cell'}.find do |el|
|
349
|
-
# curr_coli += (Tools.get_ns_attribute_value(el, 'table', 'number-columns-repeated') || 1).to_i
|
350
|
-
# curr_coli > coli
|
351
|
-
# end
|
352
|
-
# unless cellnode.nil?
|
353
|
-
# return cellnode
|
354
|
-
# else
|
355
|
-
# add_cell
|
356
|
-
# end
|
357
|
-
# end
|
358
|
-
# end
|
359
|
-
# def add_cell(repeated=1)
|
360
|
-
# cell = Cell.new(self,first_unused_column_index)
|
361
|
-
# Tools.set_ns_attribute(cell.xmlnode,'table','number-columns-repeated',repeated) if repeated>1
|
362
|
-
# @xmlnode << cell.xmlnode
|
363
|
-
# cell
|
364
|
-
# end
|
365
|
-
# def first_unused_column_index
|
366
|
-
# 1 + @xmlnode.elements.select{|n| n.name=='table-cell'}.reduce(0) do |sum, el|
|
367
|
-
# sum + (Tools.get_ns_attribute_value(el, 'table', 'number-columns-repeated') || 1).to_i
|
368
|
-
# end
|
369
|
-
# end
|
370
|
-
# end
|
371
|
-
|
372
|
-
# class RowGroup < RowWithXMLNode
|
373
|
-
# @readonly = :yes_always
|
374
|
-
# attr_reader :range
|
375
|
-
# attr_accessor :parent_array, :xmlnode
|
376
|
-
# def initialize(aparent_array,arange,axmlnode=nil)
|
377
|
-
# super()
|
378
|
-
# @parent_array = aparent_array
|
379
|
-
# @range = arange
|
380
|
-
# if axmlnode.nil?
|
381
|
-
# axmlnode = Row.empty_row_node
|
382
|
-
# Tools.set_ns_attribute(axmlnode,'table','number-rows-repeated',range.size) if range.size>1
|
383
|
-
# end
|
384
|
-
# @xmlnode = axmlnode
|
385
|
-
# end
|
386
|
-
# # returns SingleRow if size of range is 1 and nil if it is 0 or less
|
387
|
-
# def normalize
|
388
|
-
# case range.size
|
389
|
-
# when 2..Float::INFINITY then self
|
390
|
-
# when 1 then SingleRow.new_from_rowgroup(self)
|
391
|
-
# else nil
|
392
|
-
# end
|
393
|
-
# end
|
394
|
-
# def repeated; range.size end
|
395
|
-
# def repeated?; range.size>1 end
|
396
|
-
# def range=(arange)
|
397
|
-
# @range=arange
|
398
|
-
# Tools.set_ns_attribute(@xmlnode,'table','number-rows-repeated',range.size, 1)
|
399
|
-
# end
|
400
|
-
# end
|
401
|
-
|
402
|
-
# class SingleRow < RowWithXMLNode
|
403
|
-
# @readonly = :no
|
404
|
-
# attr_accessor :xmlnode
|
405
|
-
# # index Integer
|
406
|
-
# def initialize(aparent_array,aindex,axmlnode=nil)
|
407
|
-
# super()
|
408
|
-
# @parent_array = aparent_array
|
409
|
-
# @index = aindex
|
410
|
-
# if axmlnode.nil?
|
411
|
-
# axmlnode = Row.empty_row_node
|
412
|
-
# end
|
413
|
-
# @xmlnode = axmlnode
|
414
|
-
# end
|
415
|
-
# def self.new_from_rowgroup(rg)
|
416
|
-
# anode = rg.xmlnode
|
417
|
-
# Tools.remove_ns_attribute(anode,'table','number-rows-repeated')
|
418
|
-
# SingleRow.new(rg.parent_array,rg.range.begin,anode)
|
419
|
-
# end
|
420
|
-
# def normalize; self end
|
421
|
-
# def repeated?; false end
|
422
|
-
# def repeated; 1 end
|
423
|
-
# def range; (@index..@index) end
|
424
|
-
# def detach; self end
|
425
|
-
# def row; @index end
|
426
|
-
# def still_out_of_used_range?; false end
|
427
|
-
# end
|
428
|
-
|
429
|
-
# class LazyDetachableRow < Row
|
430
|
-
# @readonly = :yes_but_detachable
|
431
|
-
# def initialize(rowi)
|
432
|
-
# super()
|
433
|
-
# @index = rowi.to_i
|
434
|
-
# end
|
435
|
-
# def add_cell; detach.add_cell end
|
436
|
-
# def style_name=(value); detach.style_name=value end
|
437
|
-
# def row; @index end
|
438
|
-
# end
|
439
|
-
|
440
|
-
# ## there are not data in this object, they are taken from RowGroup, but this is only readonly
|
441
|
-
# class MemberOfRowGroup < LazyDetachableRow
|
442
|
-
# @readonly = :yes_but_detachable
|
443
|
-
# extend Forwardable
|
444
|
-
# delegate [:repeated?, :repeated, :xmlnode, :parent_array] => :@row_group
|
445
|
-
# attr_accessor :row_group # for dubugging
|
446
|
-
#
|
447
|
-
# # @index Integer
|
448
|
-
# # @row_group RepeatedRow
|
449
|
-
# def initialize(arowi,arow_group)
|
450
|
-
# super(arowi)
|
451
|
-
# @row_group = arow_group
|
452
|
-
# raise 'Wrong parameter given - class is '+@row_group.class.to_a unless @row_group.is_a? RowGroup
|
453
|
-
# end
|
454
|
-
# def detach # detaches MemberOfRowGroup from its RowGroup perhaps splitting RowGroup
|
455
|
-
# @row_group.parent_array.detach(@index)
|
456
|
-
# end
|
457
|
-
# def get_cell(coli)
|
458
|
-
# c = Cell.new(self,coli,@row_group.cellnodes(coli))
|
459
|
-
# c.mode = :repeated
|
460
|
-
# c
|
461
|
-
# end
|
462
|
-
# def first_unused_column_index
|
463
|
-
# @row_group.first_unused_column_index
|
464
|
-
# end
|
465
|
-
# def nonemptycells
|
466
|
-
# @row_group.nonemptycellsindexes.collect{ |coli| cells(coli) }
|
467
|
-
# end
|
468
|
-
# end
|
469
|
-
|
470
|
-
|
471
123
|
end
|
data/lib/rspreadsheet/version.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'rspreadsheet/row'
|
2
|
+
require 'rspreadsheet/column'
|
2
3
|
require 'rspreadsheet/tools'
|
4
|
+
require 'helpers/class_extensions'
|
3
5
|
# require 'forwardable'
|
4
6
|
|
5
7
|
module Rspreadsheet
|
@@ -54,6 +56,7 @@ class Worksheet
|
|
54
56
|
|
55
57
|
#@!group XMLTiedArray connected methods
|
56
58
|
def rows(*params); subitems(*params) end
|
59
|
+
alias :row :rows
|
57
60
|
def prepare_subitem(rowi); Row.new(self,rowi) end
|
58
61
|
def rowcache; @itemcache end
|
59
62
|
|
@@ -82,10 +85,15 @@ class Worksheet
|
|
82
85
|
when 0 then raise 'Not implemented yet' #TODO: return list of all cells
|
83
86
|
when 1..2
|
84
87
|
r,c = Rspreadsheet::Tools.a2c(*params)
|
85
|
-
|
88
|
+
row(r).andand.cell(c)
|
86
89
|
else raise Exception.new('Wrong number of arguments.')
|
87
90
|
end
|
88
91
|
end
|
92
|
+
alias :cell :cells
|
93
|
+
def column(param)
|
94
|
+
r,coli = Rspreadsheet::Tools.a2c(1,param)
|
95
|
+
Column.new(self,coli)
|
96
|
+
end
|
89
97
|
# Allows syntax like sheet.F15. TO catch errors easier, allows only up to three uppercase letters in colum part, althought it won't be necessarry to restrict.
|
90
98
|
def method_missing method_name, *args, &block
|
91
99
|
if method_name.to_s.match(/^([A-Z]{1,3})(\d{1,8})(=?)$/)
|
@@ -1,5 +1,9 @@
|
|
1
|
+
require 'helpers/class_extensions'
|
2
|
+
|
1
3
|
module Rspreadsheet
|
2
4
|
|
5
|
+
using ClassExtensions if RUBY_VERSION > '2.1'
|
6
|
+
|
3
7
|
# @private
|
4
8
|
class XMLTied
|
5
9
|
def xml
|
@@ -66,8 +70,8 @@ end
|
|
66
70
|
|
67
71
|
# abstract class. All importers MUST implement: prepare_subitem (and delete)
|
68
72
|
# terminology
|
69
|
-
#
|
70
|
-
#
|
73
|
+
# item, subitem is object from @itemcache (quite often subclass of XMLTiedItem)
|
74
|
+
# node, subnode is LibXML::XML::Node object
|
71
75
|
#
|
72
76
|
# this class is made to be included, not subclassed - the reason is in delete method which calls super
|
73
77
|
# @private
|
data/spec/cell_spec.rb
CHANGED
@@ -8,115 +8,119 @@ describe Rspreadsheet::Cell do
|
|
8
8
|
@sheet2 = book2.worksheets(1)
|
9
9
|
end
|
10
10
|
it 'contains good row and col coordinates' do
|
11
|
-
@cell = @sheet1.
|
11
|
+
@cell = @sheet1.cell(1,3)
|
12
12
|
@cell.rowi.should == 1
|
13
13
|
@cell.coli.should == 3
|
14
14
|
@cell.coordinates.should == [1,3]
|
15
|
-
@cell = @sheet2.
|
15
|
+
@cell = @sheet2.cell(7,2)
|
16
16
|
@cell.rowi.should == 7
|
17
17
|
@cell.coli.should == 2
|
18
18
|
@cell.coordinates.should == [7,2]
|
19
19
|
end
|
20
20
|
it 'can be referenced by more vars and both are synchronized' do
|
21
|
-
@cell = @sheet1.
|
21
|
+
@cell = @sheet1.cell(1,1)
|
22
22
|
@sheet1[1,1] = 'novinka'
|
23
23
|
@cell.value.should == 'novinka'
|
24
24
|
end
|
25
25
|
it 'can be modified by more ways and all are identical' do
|
26
|
-
@cell = @sheet1.
|
26
|
+
@cell = @sheet1.cell(2,2)
|
27
27
|
@sheet1[2,2] = 'zaprve'
|
28
28
|
@cell.value.should == 'zaprve'
|
29
|
-
@sheet1.
|
29
|
+
@sheet1.cell(2,2).value = 'zadruhe'
|
30
30
|
@cell.value.should == 'zadruhe'
|
31
31
|
@sheet1.B2 = 'zatreti'
|
32
32
|
@cell.value.should == 'zatreti'
|
33
|
-
@sheet1.
|
33
|
+
@sheet1.row(2).cell(2).value = 'zactvrte'
|
34
34
|
@cell.value.should == 'zactvrte'
|
35
|
-
@sheet1.rows(2)
|
35
|
+
@sheet1.rows[1].cell(2).value = 'zactvrte s arrayem'
|
36
|
+
@cell.value.should == 'zactvrte s arrayem'
|
37
|
+
@sheet1.row(2)[2] = 'zapate'
|
36
38
|
@cell.value.should == 'zapate'
|
39
|
+
@sheet1.rows[1][2] = 'zaseste' ## tohle je divoka moznost
|
40
|
+
@cell.value.should == 'zaseste'
|
37
41
|
end
|
38
42
|
it 'can include links' do
|
39
43
|
@sheet2.A12.should == '[http://example.org/]'
|
40
|
-
@sheet2.
|
41
|
-
@sheet2.
|
42
|
-
@sheet2.
|
44
|
+
@sheet2.cell(12,2).valuexmlfindall('.//text:a').size.should eq 0
|
45
|
+
@sheet2.cell(12,1).valuexmlfindall('.//text:a').size.should eq 1
|
46
|
+
@sheet2.cell(12,1).valuexmlfindfirst('.//text:a').attributes['href'].should eq 'http://example.org/'
|
43
47
|
end
|
44
48
|
it 'contains good row and col coordinates even after table:number-columns-repeated cells' do
|
45
|
-
@cell = @sheet2.
|
49
|
+
@cell = @sheet2.cell(13,5)
|
46
50
|
@cell.value.should == 'afterrepeated'
|
47
51
|
@cell.rowi.should == 13
|
48
52
|
@cell.coli.should == 5
|
49
53
|
end
|
50
54
|
it 'reports good range of coordinates for repeated cells' do
|
51
|
-
@cell = @sheet2.
|
55
|
+
@cell = @sheet2.cell(13,2)
|
52
56
|
@cell.range.should == (1..4)
|
53
57
|
@cell.mode.should == :repeated
|
54
58
|
end
|
55
59
|
it 'returns nil on negative and zero cell indexes or raises exception depending on configuration' do
|
56
60
|
pom = Rspreadsheet.raise_on_negative_coordinates
|
57
61
|
# default is to raise error
|
58
|
-
expect {@sheet2.
|
62
|
+
expect {@sheet2.cell(0,5) }.to raise_error
|
59
63
|
|
60
64
|
# return nil if configured to do so
|
61
65
|
Rspreadsheet.raise_on_negative_coordinates = false
|
62
|
-
@sheet2.
|
63
|
-
@sheet2.
|
64
|
-
@sheet2.
|
66
|
+
@sheet2.cell(0,5).should be_nil
|
67
|
+
@sheet2.cell(2,-5).should be(nil)
|
68
|
+
@sheet2.cell(-2,-5).should be(nil)
|
65
69
|
|
66
70
|
Rspreadsheet.raise_on_negative_coordinates = pom # reset the setting back
|
67
71
|
end
|
68
72
|
it 'has nonempty parents' do
|
69
|
-
@cell = @sheet2.
|
73
|
+
@cell = @sheet2.cell(13,5)
|
70
74
|
@cell.row.should_not be_nil
|
71
75
|
@cell.worksheet.should_not be_nil
|
72
76
|
|
73
|
-
@cell = @sheet1.
|
77
|
+
@cell = @sheet1.cell(2,2)
|
74
78
|
@cell.row.should_not be_nil
|
75
79
|
@cell.worksheet.should_not be_nil
|
76
80
|
end
|
77
81
|
it 'handles relative correctly' do
|
78
|
-
@sheet2.
|
79
|
-
@sheet2.
|
82
|
+
@sheet2.cell(3,3).relative(-1,+2).coordinates.should == [2,5]
|
83
|
+
@sheet2.cell(3,3).relative(0,0).coordinates.should == [3,3]
|
80
84
|
end
|
81
85
|
it 'is automatically "unrepeated" on value assignement' do
|
82
|
-
@cell = @sheet2.
|
86
|
+
@cell = @sheet2.cell(13,2)
|
83
87
|
@cell.is_repeated?.should == true
|
84
88
|
@cell.value = 'cokoli'
|
85
89
|
@cell.is_repeated?.should == false
|
86
90
|
@cell.value.should == 'cokoli'
|
87
|
-
@sheet2.
|
88
|
-
@sheet2.
|
89
|
-
@sheet2.
|
91
|
+
@sheet2.cell(13,1).should_not == 'cokoli'
|
92
|
+
@sheet2.cell(13,3).should_not == 'cokoli'
|
93
|
+
@sheet2.cell(13,4).should_not == 'cokoli'
|
90
94
|
end
|
91
95
|
it 'returns correct type for the cell' do
|
92
|
-
@sheet2.
|
93
|
-
@sheet2.
|
94
|
-
@sheet2.
|
95
|
-
@sheet2.
|
96
|
-
@sheet2.
|
97
|
-
@sheet2.
|
98
|
-
@sheet2.
|
99
|
-
@sheet2.
|
96
|
+
@sheet2.cell(1,2).type.should eq :string
|
97
|
+
@sheet2.cell(2,2).type.should eq :date
|
98
|
+
@sheet2.cell(3,1).type.should eq :float
|
99
|
+
@sheet2.cell(3,2).type.should eq :percentage
|
100
|
+
@sheet2.cell(4,2).type.should eq :string
|
101
|
+
@sheet2.cell('B22').type.should eq :currency
|
102
|
+
@sheet2.cell('B23').type.should eq :currency
|
103
|
+
@sheet2.cell(200,200).type.should eq :unassigned
|
100
104
|
end
|
101
105
|
it 'returns value of correct type' do
|
102
106
|
@sheet2[1,2].should be_kind_of(String)
|
103
107
|
@sheet2[2,2].should be_kind_of(Date)
|
104
108
|
@sheet2[3,1].should be_kind_of(Float)
|
105
109
|
@sheet2[3,2].should be_kind_of(Float)
|
106
|
-
@sheet2.
|
107
|
-
@sheet2.
|
108
|
-
@sheet2.
|
110
|
+
@sheet2.cell(3,2).type.should eq :percentage
|
111
|
+
@sheet2.cell(3,2).guess_cell_type.should eq :percentage
|
112
|
+
@sheet2.cell(3,2).guess_cell_type(1).should eq :percentage
|
109
113
|
@sheet2[3,2]=0.1
|
110
|
-
@sheet2.
|
114
|
+
@sheet2.cell(3,2).type.should eq :percentage
|
111
115
|
@sheet2[4,2].should be_kind_of(String)
|
112
116
|
end
|
113
117
|
it 'is the same object no matter how you access it' do
|
114
|
-
@cell1 = @sheet2.
|
115
|
-
@cell2 = @sheet2.rows(5).
|
118
|
+
@cell1 = @sheet2.cell(5,5)
|
119
|
+
@cell2 = @sheet2.rows(5).cell(5)
|
116
120
|
@cell1.should equal(@cell2)
|
117
121
|
end
|
118
122
|
it 'splits correctly cells if written in the middle of repeated group' do
|
119
|
-
@cell = @sheet2.
|
123
|
+
@cell = @sheet2.cell(4,6)
|
120
124
|
@cell.range.should == (4..7)
|
121
125
|
@cell.value.should == 7
|
122
126
|
|
@@ -124,16 +128,16 @@ describe Rspreadsheet::Cell do
|
|
124
128
|
@cell.range.should == (6..6)
|
125
129
|
@cell.value.should == 'nebesa'
|
126
130
|
|
127
|
-
@cellA = @sheet2.
|
131
|
+
@cellA = @sheet2.cell(4,5)
|
128
132
|
@cellA.range.should == (4..5)
|
129
133
|
@cellA.value.should == 7
|
130
134
|
|
131
|
-
@cellB = @sheet2.
|
135
|
+
@cellB = @sheet2.cell(4,7)
|
132
136
|
@cellB.range.should == (7..7)
|
133
137
|
@cellB.value.should == 7
|
134
138
|
end
|
135
139
|
it 'inserts correctly cell in the middle of repeated group' do
|
136
|
-
@cell = @sheet2.
|
140
|
+
@cell = @sheet2.cell(4,6)
|
137
141
|
@cell.range.should == (4..7)
|
138
142
|
@cell.value.should == 7
|
139
143
|
@cell.coli.should == 6
|
@@ -141,15 +145,15 @@ describe Rspreadsheet::Cell do
|
|
141
145
|
@sheet2.insert_cell_before(4,6)
|
142
146
|
@cell.coli.should == 7
|
143
147
|
|
144
|
-
@cellA = @sheet2.
|
148
|
+
@cellA = @sheet2.cell(4,5)
|
145
149
|
@cellA.range.should == (4..5)
|
146
150
|
@cellA.value.should == 7
|
147
151
|
|
148
|
-
@cellB = @sheet2.
|
152
|
+
@cellB = @sheet2.cell(4,7)
|
149
153
|
@cellB.range.should == (7..8)
|
150
154
|
@cellB.value.should == 7
|
151
155
|
|
152
|
-
@cell = @sheet2.
|
156
|
+
@cell = @sheet2.cell(16,4)
|
153
157
|
@cell.range.should == (1..7)
|
154
158
|
@cell.value.should == nil
|
155
159
|
|
@@ -157,45 +161,45 @@ describe Rspreadsheet::Cell do
|
|
157
161
|
@sheet2.rows(16).range.should == (14..18)
|
158
162
|
@sheet2.rows(17).range.should == (14..18)
|
159
163
|
@sheet2.insert_cell_before(16,3)
|
160
|
-
@sheet2.
|
161
|
-
@sheet2.
|
164
|
+
@sheet2.cell(16,3).value = 'baf'
|
165
|
+
@sheet2.cell(17,3).value.should_not == 'baf'
|
162
166
|
@sheet2.rows(15).range.should == (14..15)
|
163
167
|
@sheet2.rows(16).range.should == (16..16)
|
164
168
|
@sheet2.rows(17).range.should == (17..18)
|
165
169
|
|
166
|
-
@cellA = @sheet2.
|
170
|
+
@cellA = @sheet2.cell(16,1)
|
167
171
|
@cellA.range.should == (1..2)
|
168
172
|
@cellA.value.should be_nil
|
169
173
|
|
170
|
-
@cellB = @sheet2.
|
174
|
+
@cellB = @sheet2.cell(16,5)
|
171
175
|
@cellB.range.should == (4..8)
|
172
176
|
@cellB.value.should be_nil
|
173
177
|
|
174
178
|
end
|
175
179
|
it 'inserted has correct class' do # based on real error
|
176
180
|
@sheet2.insert_cell_before(1,1)
|
177
|
-
@sheet2.rows(1).
|
181
|
+
@sheet2.rows(1).cell(1).should be_kind_of(Rspreadsheet::Cell)
|
178
182
|
end
|
179
183
|
it 'can have different formats' do
|
180
|
-
@cell = @sheet2.
|
184
|
+
@cell = @sheet2.cell(6,3)
|
181
185
|
@cell.format.bold.should == true
|
182
|
-
@cell = @sheet2.
|
186
|
+
@cell = @sheet2.cell(6,4)
|
183
187
|
@cell.format.bold.should == false
|
184
188
|
@cell.format.italic.should == true
|
185
|
-
@cell = @sheet2.
|
189
|
+
@cell = @sheet2.cell(6,5)
|
186
190
|
@cell.format.italic.should == false
|
187
191
|
@cell.format.color.should == '#ff3333'
|
188
|
-
@cell = @sheet2.
|
192
|
+
@cell = @sheet2.cell(6,6)
|
189
193
|
@cell.format.color.should_not == '#ff3333'
|
190
194
|
@cell.format.background_color.should == '#6666ff'
|
191
|
-
@cell = @sheet2.
|
195
|
+
@cell = @sheet2.cell(6,7)
|
192
196
|
@cell.format.font_size.should == '7pt'
|
193
197
|
|
194
198
|
# after fresh create
|
195
199
|
@cell.xmlnode.attributes['style-name'].should_not be_nil
|
196
200
|
end
|
197
|
-
it 'can set formats of the
|
198
|
-
@cell = @sheet1.
|
201
|
+
it 'can set formats of the cell in new file' do
|
202
|
+
@cell = @sheet1.cell(1,1)
|
199
203
|
@cell.value = '1'
|
200
204
|
# bold
|
201
205
|
@cell.format.bold.should be_falsey
|
@@ -226,15 +230,15 @@ describe Rspreadsheet::Cell do
|
|
226
230
|
|
227
231
|
end
|
228
232
|
it 'changes coordinates when row inserted above' do
|
229
|
-
@sheet1.
|
230
|
-
@cell = @sheet1.
|
233
|
+
@sheet1.cell(2,2).detach
|
234
|
+
@cell = @sheet1.cell(2,2)
|
231
235
|
@cell.rowi.should == 2
|
232
236
|
@sheet1.add_row_above(1)
|
233
237
|
@cell.rowi.should == 3
|
234
238
|
end
|
235
239
|
it 'switches to invalid_reference cell when deleted' do
|
236
240
|
@sheet1[2,5] = 'nejaka data'
|
237
|
-
@cell = @sheet1.
|
241
|
+
@cell = @sheet1.cell(2,2)
|
238
242
|
@cell.value = 'data'
|
239
243
|
@cell.invalid_reference?.should be false
|
240
244
|
@cell.delete
|
@@ -242,11 +246,11 @@ describe Rspreadsheet::Cell do
|
|
242
246
|
expect { @cell.rowi }.to raise_error
|
243
247
|
expect { @cell.address }.to raise_error
|
244
248
|
|
245
|
-
@sheet1.
|
246
|
-
@sheet1.
|
249
|
+
@sheet1.cell(2,2).type.should == :empty
|
250
|
+
@sheet1.cell(3,2).type.should == :unassigned
|
247
251
|
end
|
248
252
|
it 'switches to invalid_reference cell when its row is deleted' do
|
249
|
-
@cell = @sheet1.
|
253
|
+
@cell = @sheet1.cell(6,2)
|
250
254
|
@cell.value = 'data'
|
251
255
|
@cell.rowi.should == 6
|
252
256
|
@sheet1.rows(6).delete
|
@@ -254,35 +258,35 @@ describe Rspreadsheet::Cell do
|
|
254
258
|
@cell.invalid_reference?.should be true
|
255
259
|
end
|
256
260
|
it 'has inspect method returning something good' do
|
257
|
-
@cell = @sheet1.
|
261
|
+
@cell = @sheet1.cell(6,2)
|
258
262
|
@cell.value = 'abcde'
|
259
263
|
expect(@cell.inspect).to include('abcde','::Cell','6','2','row')
|
260
264
|
end
|
261
265
|
it 'stores date correctly' do
|
262
|
-
@cell = @sheet1.
|
266
|
+
@cell = @sheet1.cell(1,1)
|
263
267
|
@cell.value= Date.parse('2014-01-02')
|
264
268
|
@cell.value.year.should eq 2014
|
265
269
|
@cell.value.month.should eq 1
|
266
270
|
@cell.value.day.should eq 2
|
267
271
|
end
|
268
272
|
it 'can be addressed by even more ways and all are identical' do
|
269
|
-
@cell = @sheet1.
|
270
|
-
@sheet1.
|
271
|
-
@sheet1.
|
273
|
+
@cell = @sheet1.cell(2,2)
|
274
|
+
@sheet1.cell('B2').value = 'zaseste'
|
275
|
+
@sheet1.cell('B2').value.should == 'zaseste'
|
272
276
|
@cell.value.should == 'zaseste'
|
273
|
-
@sheet1.
|
274
|
-
@sheet1.
|
277
|
+
@sheet1.cell(2,'B').value.should == 'zaseste'
|
278
|
+
@sheet1.cell(2,'B').value = 'zasedme'
|
275
279
|
@cell.value.should == 'zasedme'
|
276
280
|
@sheet1['B2'].should == 'zasedme'
|
277
281
|
@sheet1['B2'] = 'zaosme'
|
278
282
|
@cell.value.should == 'zaosme'
|
279
283
|
|
280
|
-
@sheet2.
|
281
|
-
@sheet2.
|
282
|
-
@sheet2.
|
284
|
+
@sheet2.cell('F2').should be @sheet2.cell(2,6)
|
285
|
+
@sheet2.cell('BA177').should be @sheet2.cell(177,53)
|
286
|
+
@sheet2.cell('ADA2').should be @sheet2.cell(2,781)
|
283
287
|
end
|
284
288
|
it 'setting format in new file detaches the cell' do
|
285
|
-
@cell = @sheet1.
|
289
|
+
@cell = @sheet1.cell(1,1)
|
286
290
|
# bold
|
287
291
|
@cell.format.bold.should be_falsey
|
288
292
|
@cell.format.bold = true
|
@@ -290,14 +294,14 @@ describe Rspreadsheet::Cell do
|
|
290
294
|
@cell.mode.should eq :regular
|
291
295
|
end
|
292
296
|
it 'remembers formula when set' do
|
293
|
-
@cell = @sheet1.
|
297
|
+
@cell = @sheet1.cell(1,1)
|
294
298
|
# bold
|
295
299
|
@cell.formula.should be_nil
|
296
300
|
@cell.formula='=1+5'
|
297
301
|
@cell.formula.should eq '=1+5'
|
298
302
|
end
|
299
303
|
it 'unsets cell type when formula set - we can not guess it correctly', :focus do
|
300
|
-
@cell = @sheet1.
|
304
|
+
@cell = @sheet1.cell(1,1)
|
301
305
|
@cell.value = 'ahoj'
|
302
306
|
@cell.type.should eq :string
|
303
307
|
@cell.formula='=1+5'
|
@@ -306,7 +310,7 @@ describe Rspreadsheet::Cell do
|
|
306
310
|
@cell.type.should eq :empty
|
307
311
|
end
|
308
312
|
it 'wipes out formula after assiging value' do
|
309
|
-
@cell = @sheet1.
|
313
|
+
@cell = @sheet1.cell(1,1)
|
310
314
|
@cell.formula='=1+5'
|
311
315
|
@cell.formula.should_not be_nil
|
312
316
|
@cell.value = 'baf'
|
@@ -314,19 +318,13 @@ describe Rspreadsheet::Cell do
|
|
314
318
|
@cell.formula.should be_nil
|
315
319
|
end
|
316
320
|
it 'works well with currency types' do
|
317
|
-
@usdcell = @sheet2.
|
321
|
+
@usdcell = @sheet2.cell('B22')
|
318
322
|
@usdcell.type.should eq :currency
|
319
323
|
@usdcell.value.should == -147984.84
|
320
324
|
@usdcell.format.currency.should == 'USD'
|
321
325
|
|
322
|
-
@czkcell = @sheet2.
|
326
|
+
@czkcell = @sheet2.cell('B23')
|
323
327
|
@czkcell.value.should == 344.to_d
|
324
328
|
@czkcell.format.currency.should == 'CZK'
|
325
329
|
end
|
326
330
|
end
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
if RUBY_VERSION > '2.1'
|
4
|
+
using ClassExtensions
|
5
|
+
|
6
|
+
describe Array do
|
7
|
+
it 'can sum simple array' do
|
8
|
+
a = [1,2,3,4]
|
9
|
+
a.sum.should == 10
|
10
|
+
end
|
11
|
+
it 'ignores text and nils while summing' do
|
12
|
+
a = [1,nil, nil,2,3,'foo',5.0]
|
13
|
+
a.sum.should == 11
|
14
|
+
[nil, 'nic'].sum.should == 0
|
15
|
+
[].sum.should == 0
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe LibXML::XML::Node do
|
20
|
+
before do
|
21
|
+
@n = LibXML::XML::Node.new('a')
|
22
|
+
@n << LibXML::XML::Node.new('i','italic')
|
23
|
+
b = LibXML::XML::Node.new('p','paragraph')
|
24
|
+
b << LibXML::XML::Node.new('b','boldtext')
|
25
|
+
@n << b
|
26
|
+
@n << LibXML::XML::Node.new_text('textnode')
|
27
|
+
|
28
|
+
@m = LibXML::XML::Node.new('a')
|
29
|
+
@m << LibXML::XML::Node.new('i','italic')
|
30
|
+
c = LibXML::XML::Node.new('p','paragraph')
|
31
|
+
c << LibXML::XML::Node.new('b','boldtext')
|
32
|
+
@m << c
|
33
|
+
@m << LibXML::XML::Node.new_text('textnode')
|
34
|
+
|
35
|
+
@m2 = LibXML::XML::Node.new('a')
|
36
|
+
end
|
37
|
+
it 'can compare nodes' do
|
38
|
+
@n.should == @m
|
39
|
+
@n.should_not == @m2
|
40
|
+
end
|
41
|
+
it 'has correct elements' do
|
42
|
+
# raise @n.first_diff(@m).inspect
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
data/spec/column_spec.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rspreadsheet::Cell do
|
4
|
+
before do
|
5
|
+
book1 = Rspreadsheet.new
|
6
|
+
@sheet = book1.create_worksheet
|
7
|
+
end
|
8
|
+
it 'contains the right cells' do
|
9
|
+
@sheet[1,1] = '11'
|
10
|
+
@sheet[1,2] = '12'
|
11
|
+
@sheet[2,1] = '21'
|
12
|
+
@sheet[2,2] = '22'
|
13
|
+
@sheet.column(1).cell(2).value.should eq('21')
|
14
|
+
@sheet.column(2).cell(2).value.should eq('22')
|
15
|
+
@sheet.column(2).cell(1).should eq(@sheet.cell(1,2))
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
data/spec/rspreadsheet_spec.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
using ClassExtensions if RUBY_VERSION > '2.1'
|
2
3
|
|
3
4
|
describe Rspreadsheet do
|
4
5
|
it 'can open ods testfile and reads its content correctly' do
|
@@ -42,7 +43,7 @@ describe Rspreadsheet do
|
|
42
43
|
@content_xml2.root.first_diff(@content_xml1.root).should be_nil
|
43
44
|
@content_xml1.root.first_diff(@content_xml2.root).should be_nil
|
44
45
|
|
45
|
-
@content_xml1.root.
|
46
|
+
@content_xml1.root.should == @content_xml2.root
|
46
47
|
end
|
47
48
|
it 'when open and save file modified, than the file is different' do
|
48
49
|
tmp_filename = '/tmp/testfile1.ods' # first delete temp file
|
@@ -87,7 +88,7 @@ describe Rspreadsheet do
|
|
87
88
|
sheet.cells(5,2).format.bold = true
|
88
89
|
sheet.cells(5,2).format.background_color = '#FF0000'
|
89
90
|
}.not_to raise_error
|
90
|
-
|
91
|
+
|
91
92
|
sheet.rows(4).cellvalues.sum{|val| val.to_f}.should eq 4+7*4
|
92
93
|
sheet.rows(4).cells.sum{ |cell| cell.value.to_f }.should eq 4+7*4
|
93
94
|
|
data/spec/worksheet_spec.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspreadsheet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.14
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jakub A.Těšínský
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-11-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: libxml-ruby
|
@@ -180,6 +180,7 @@ files:
|
|
180
180
|
- lib/helpers/configuration.rb
|
181
181
|
- lib/rspreadsheet.rb
|
182
182
|
- lib/rspreadsheet/cell.rb
|
183
|
+
- lib/rspreadsheet/column.rb
|
183
184
|
- lib/rspreadsheet/empty_file_template.ods
|
184
185
|
- lib/rspreadsheet/row.rb
|
185
186
|
- lib/rspreadsheet/tools.rb
|
@@ -190,6 +191,8 @@ files:
|
|
190
191
|
- reinstall_local_gem.sh
|
191
192
|
- rspreadsheet.gemspec
|
192
193
|
- spec/cell_spec.rb
|
194
|
+
- spec/class_extensions_spec.rb
|
195
|
+
- spec/column_spec.rb
|
193
196
|
- spec/row_spec.rb
|
194
197
|
- spec/rspreadsheet_spec.rb
|
195
198
|
- spec/spec_helper.rb
|
@@ -224,6 +227,8 @@ summary: Manipulating spreadsheets with Ruby (read / create / modify OpenDocumen
|
|
224
227
|
Spreadsheet).
|
225
228
|
test_files:
|
226
229
|
- spec/cell_spec.rb
|
230
|
+
- spec/class_extensions_spec.rb
|
231
|
+
- spec/column_spec.rb
|
227
232
|
- spec/row_spec.rb
|
228
233
|
- spec/rspreadsheet_spec.rb
|
229
234
|
- spec/spec_helper.rb
|