rspreadsheet 0.2.15 → 0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +4 -1
- data/.travis.yml +5 -4
- data/DEVEL_BLOG.md +18 -2
- data/GUIDE.md +5 -0
- data/Guardfile +3 -3
- data/README.md +9 -9
- data/lib/helpers/class_extensions.rb +101 -38
- data/lib/rspreadsheet.rb +2 -1
- data/lib/rspreadsheet/cell.rb +110 -19
- data/lib/rspreadsheet/image.rb +118 -0
- data/lib/rspreadsheet/row.rb +14 -369
- data/lib/rspreadsheet/tools.rb +20 -1
- data/lib/rspreadsheet/version.rb +1 -1
- data/lib/rspreadsheet/workbook.rb +81 -20
- data/lib/rspreadsheet/worksheet.rb +31 -10
- data/lib/rspreadsheet/xml_tied_array.rb +179 -0
- data/lib/rspreadsheet/xml_tied_item.rb +111 -0
- data/lib/rspreadsheet/xml_tied_repeatable.rb +151 -0
- data/reinstall_local_gem.sh +2 -2
- data/rspreadsheet.gemspec +15 -3
- data/spec/cell_spec.rb +82 -1
- data/spec/class_extensions_spec.rb +49 -0
- data/spec/image_spec.rb +104 -0
- data/spec/included_image_spec.rb +8 -0
- data/spec/io_spec.rb +39 -27
- data/spec/row_spec.rb +16 -1
- data/spec/rspreadsheet_spec.rb +94 -35
- data/spec/spec_helper.rb +0 -1
- data/spec/test-image-blue.png +0 -0
- data/spec/test-image.png +0 -0
- data/spec/testfile1.ods +0 -0
- data/spec/testfile2-images.ods +0 -0
- data/spec/workbook_spec.rb +27 -0
- data/spec/worksheet_spec.rb +9 -2
- metadata +20 -47
- data/investigate.rb +0 -19
- data/lib/rspreadsheet/xml_tied.rb +0 -253
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'helpers/class_extensions'
|
2
|
+
require 'rspreadsheet/xml_tied_array' # BASTL to je jen kvuli XMLTied class
|
3
|
+
|
4
|
+
module Rspreadsheet
|
5
|
+
|
6
|
+
using ClassExtensions if RUBY_VERSION > '2.1'
|
7
|
+
|
8
|
+
# Note for developers: In case you want to represent a node containing many identical subnodes
|
9
|
+
# (like row contains cells, worksheet contains rows or images part includes images) than
|
10
|
+
# 1. Include module XMLTiedArray into parent class
|
11
|
+
# 2. Subclass XMLTiedItem by object which will represent individual items
|
12
|
+
# 3. Implement methods which are mentioned at both of these and the rest should work itself.
|
13
|
+
# 4. prepare_subitem calls method new of the item and usually sends index and parent + some
|
14
|
+
# more so subitem can implement parent and index methods.
|
15
|
+
|
16
|
+
# @private
|
17
|
+
# abstract class. All successors have some options. MUST implement:
|
18
|
+
# * xml_options
|
19
|
+
#
|
20
|
+
# If you override intializer make sure you call initialize_xml_tied_item(aparent,aindex).
|
21
|
+
#
|
22
|
+
# Optionally you may implement method which makes index accessible under more meaningful name
|
23
|
+
# like column or so. Optionally you may implement method which makes parent accessible under
|
24
|
+
# more meaningful name like worksheet or so.
|
25
|
+
#
|
26
|
+
# By default parent is stored at initialization and never changed. If you do not want to cache
|
27
|
+
# it like this (to prevent inconsistencies) just override parent method to dfind the parent
|
28
|
+
# dynamically (see Cell). If you do so you may want to disable the default parent handling by
|
29
|
+
# calling initialize_xml_tied_item(nil,index) in initializer (or do not call it at all if you
|
30
|
+
# have overridden index as well.
|
31
|
+
#
|
32
|
+
# Note: If there is a object representing parent (presumably using XMLTiedArray) than initialize
|
33
|
+
# signature must be reflected in parent prepare_subitem method
|
34
|
+
#
|
35
|
+
class XMLTiedItem < XMLTied
|
36
|
+
|
37
|
+
def initialize(aparent,aindex)
|
38
|
+
initialize_xml_tied_item(aparent,aindex)
|
39
|
+
end
|
40
|
+
def parent; @xml_tied_parent end
|
41
|
+
def index; @xml_tied_item_index end
|
42
|
+
def set_index(aindex); @xml_tied_item_index=aindex end
|
43
|
+
def index=(aindex); @xml_tied_item_index=aindex end
|
44
|
+
|
45
|
+
# `xml_options[:xml_items_node_name]` gives the name of the tag representing cell
|
46
|
+
# `xml_options[:number-columns-repeated]` gives the name of the previous tag which sais how many times the item is repeated
|
47
|
+
def xml_options; abstract end
|
48
|
+
|
49
|
+
def initialize_xml_tied_item(aparent,aindex)
|
50
|
+
@xml_tied_parent = aparent unless aparent.nil?
|
51
|
+
@xml_tied_item_index = aindex unless aindex.nil?
|
52
|
+
end
|
53
|
+
|
54
|
+
def mode
|
55
|
+
case
|
56
|
+
when xmlnode.nil? then :outbound
|
57
|
+
when repeated>1 then :repeated
|
58
|
+
else :regular
|
59
|
+
end
|
60
|
+
end
|
61
|
+
def repeated; (Tools.get_ns_attribute_value(xmlnode, 'table', xml_options[:xml_repeated_attribute]) || 1 ).to_i end
|
62
|
+
def repeated?; mode==:repeated || mode==:outbound end
|
63
|
+
alias :is_repeated? :repeated?
|
64
|
+
def xmlnode
|
65
|
+
if parent.xmlnode.nil?
|
66
|
+
nil
|
67
|
+
else
|
68
|
+
parent.my_subnode(index)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
def detach_if_needed
|
72
|
+
detach if repeated? # item did not exist individually yet, detach it within its parent and therefore make it individally editable
|
73
|
+
end
|
74
|
+
def detach
|
75
|
+
parent.detach_if_needed if parent.respond_to?(:detach_if_needed)
|
76
|
+
parent.detach_my_subnode_respect_repeated(index)
|
77
|
+
self
|
78
|
+
end
|
79
|
+
def _shift_by(diff)
|
80
|
+
set_index(index + diff)
|
81
|
+
end
|
82
|
+
def range
|
83
|
+
parent.my_subnode_range(index)
|
84
|
+
end
|
85
|
+
def invalid_reference?; false end
|
86
|
+
# destroys the object so it can not be used, this is necessarry to prevent
|
87
|
+
# accessing cells and rows which has been long time ago deleted and do not represent
|
88
|
+
# any physical object anymore
|
89
|
+
def invalidate_myself
|
90
|
+
raise_destroyed_cell_error = Proc.new {|*params| raise "Calling method of already destroyed Cell."}
|
91
|
+
(self.methods - Object.methods + [:nil?]).each do |method| # "undefine" all methods
|
92
|
+
self.singleton_class.send(:define_method, method, raise_destroyed_cell_error)
|
93
|
+
end
|
94
|
+
self.singleton_class.send(:define_method, :inspect, -> { "#<%s:0x%x destroyed cell>" % [self.class,object_id] }) # define descriptive inspect
|
95
|
+
self.singleton_class.send(:define_method, :invalid_reference?, -> { true }) # define invalid_reference? method
|
96
|
+
# invalidate variables
|
97
|
+
@xml_tied_parent=nil
|
98
|
+
@xml_tied_item_index=nil
|
99
|
+
# self.instance_variables.each do |variable|
|
100
|
+
# instance_variable_set(variable,nil)
|
101
|
+
# end
|
102
|
+
end
|
103
|
+
def delete
|
104
|
+
parent.delete_subitem(index)
|
105
|
+
invalidate_myself
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'helpers/class_extensions'
|
2
|
+
require 'rspreadsheet/xml_tied_array'
|
3
|
+
|
4
|
+
module Rspreadsheet
|
5
|
+
|
6
|
+
using ClassExtensions if RUBY_VERSION > '2.1'
|
7
|
+
|
8
|
+
|
9
|
+
# Abstract class similar to XMLTiedArray but with support to "repeatable" items. This is notion specific
|
10
|
+
# to OpenDocument files - whnewer a row repeats more times (or a cell), one can either make many identical
|
11
|
+
# copies of the same xml or only make one xml representing one item and add attribute xml_repeated.
|
12
|
+
#
|
13
|
+
# This class is made to be included, not subclassed - the reason is in delete method which calls super.
|
14
|
+
# This class is also made to handle automatic creation of outbound items.
|
15
|
+
# @private
|
16
|
+
|
17
|
+
module XMLTiedArray_WithRepeatableItems
|
18
|
+
include XMLTiedArray
|
19
|
+
|
20
|
+
def my_subnode_range(aindex)
|
21
|
+
node, range = find_subnode_with_range(aindex)
|
22
|
+
return range
|
23
|
+
end
|
24
|
+
|
25
|
+
# vrátí xmlnode na souřadnici aindex
|
26
|
+
def my_subnode(aindex)
|
27
|
+
result1, result2 = find_subnode_with_range(aindex)
|
28
|
+
return result1
|
29
|
+
end
|
30
|
+
|
31
|
+
def find_subnode_with_range(aindex)
|
32
|
+
options = subitem_xml_options
|
33
|
+
rightindex = 0
|
34
|
+
xmlsubnodes.each do |node|
|
35
|
+
repeated = (node.attributes[options[:xml_repeated_attribute]] || 1).to_i
|
36
|
+
leftindex = rightindex + 1
|
37
|
+
rightindex = rightindex+repeated
|
38
|
+
if rightindex>= aindex
|
39
|
+
return node, leftindex..rightindex
|
40
|
+
end
|
41
|
+
end
|
42
|
+
return nil, rightindex+1..Float::INFINITY
|
43
|
+
end
|
44
|
+
|
45
|
+
# @!group inserting new subnodes
|
46
|
+
|
47
|
+
def insert_new_empty_subnode_before(aindex)
|
48
|
+
insert_new_empty_subnode_before_respect_repeatable(aindex)
|
49
|
+
end
|
50
|
+
|
51
|
+
def insert_new_empty_subnode_before_respect_repeatable(aindex)
|
52
|
+
axmlnode = xmlnode
|
53
|
+
options = subitem_xml_options
|
54
|
+
node,index_range = find_subnode_with_range(aindex)
|
55
|
+
|
56
|
+
if !node.nil? # found the node, now do the insert
|
57
|
+
[index_range.begin..aindex-1,aindex..index_range.end].reject {|range| range.size<1}.each do |range| # split original node by cloning
|
58
|
+
clone_before_and_set_repeated_attribute(node,range.size,options)
|
59
|
+
end
|
60
|
+
node.prev.prev = prepare_repeated_subnode(1, options) # insert new node
|
61
|
+
node.remove! # remove the original node
|
62
|
+
else # insert outbound xmlnode
|
63
|
+
[index+1..aindex-1,aindex..aindex].reject {|range| range.size<1}.each do |range|
|
64
|
+
axmlnode << XMLTiedArray_WithRepeatableItems.prepare_repeated_subnode(range.size, options)
|
65
|
+
end
|
66
|
+
end #TODO: Out of bounds indexes handling
|
67
|
+
return my_subnode(aindex)
|
68
|
+
end
|
69
|
+
|
70
|
+
def prepare_repeated_subnode(times_repeated,options)
|
71
|
+
result = prepare_empty_subnode
|
72
|
+
Tools.set_ns_attribute(result,'table',options[:xml_repeated_attribute],times_repeated, 1)
|
73
|
+
result
|
74
|
+
end
|
75
|
+
|
76
|
+
def clone_before_and_set_repeated_attribute(node,times_repeated,options)
|
77
|
+
newnode = node.copy(true)
|
78
|
+
Tools.set_ns_attribute(newnode,'table',options[:xml_repeated_attribute],times_repeated,1)
|
79
|
+
node.prev = newnode
|
80
|
+
end
|
81
|
+
|
82
|
+
# detaches subnode with aindex
|
83
|
+
def detach_my_subnode_respect_repeated(aindex)
|
84
|
+
axmlnode = xmlnode
|
85
|
+
options = subitem_xml_options
|
86
|
+
node,index_range = find_subnode_with_range(aindex)
|
87
|
+
if index_range.size > 1 # pokud potřebuje vůbec detachovat
|
88
|
+
if !node.nil? # detach subnode
|
89
|
+
[index_range.begin..aindex-1,aindex..aindex,aindex+1..index_range.end].reject {|range| range.size<1}.each do |range| # create new structure by cloning
|
90
|
+
clone_before_and_set_repeated_attribute(node,range.size,options)
|
91
|
+
end
|
92
|
+
node.remove! # remove the original node
|
93
|
+
else # add outbound xmlnode
|
94
|
+
[index_range.begin..aindex-1,aindex..aindex].reject {|range| range.size<1}.each do |range|
|
95
|
+
axmlnode << prepare_repeated_subnode(range.size, options)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
return my_subnode(aindex)
|
100
|
+
end
|
101
|
+
|
102
|
+
def delete_my_subnode_respect_repeated(aindex)
|
103
|
+
detach_my_subnode_respect_repeated(aindex) #TODO: tohle neni uplne spravne, protoze to zanecha skupinu rozdelenou na dve casti
|
104
|
+
subitem(aindex).xmlnode.remove!
|
105
|
+
end
|
106
|
+
|
107
|
+
def how_many_times_node_is_repeated(node) # adding respect to repeated nodes
|
108
|
+
(node.attributes[subitem_xml_options[:xml_repeated_attribute]] || 1).to_i
|
109
|
+
end
|
110
|
+
|
111
|
+
|
112
|
+
# clean up item from xml (handle possible detachments) and itemcache. leave the object invalidation on the object
|
113
|
+
# this should not be called from nowhere but XMLTiedItem.delete
|
114
|
+
def delete_subitem(aindex)
|
115
|
+
options = subitem_xml_options
|
116
|
+
delete_my_subnode_respect_repeated(aindex) # vymaž node z xml
|
117
|
+
@itemcache.delete(aindex)
|
118
|
+
@itemcache.keys.sort.select{|i| i>=aindex+1 }.each do |i|
|
119
|
+
@itemcache[i-1]=@itemcache.delete(i)
|
120
|
+
@itemcache[i-1]._shift_by(-1)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def delete
|
125
|
+
@itemcache.each do |key,item|
|
126
|
+
item.delete # delete item - this destroys its subitems, xmlnode and invalidates it
|
127
|
+
@itemcache.delete(key) # delete the entry from the hash, normally this would mean this ceases to exist, if user does not have reference stored somewhere. Of he does, the object is invalidated anyways
|
128
|
+
end
|
129
|
+
super # this for example for Row objects calls XMLTiedItem.delete because Row is subclass of XMLTiedItem
|
130
|
+
end
|
131
|
+
|
132
|
+
def find_nonempty_subnode_indexes(axmlnode, options)
|
133
|
+
index = 0
|
134
|
+
result = []
|
135
|
+
axmlnode.elements.select{|node| node.name == options[:xml_items_node_name]}.each do |node|
|
136
|
+
repeated = (node.attributes[options[:xml_repeated_attribute]] || 1).to_i
|
137
|
+
index = index + repeated
|
138
|
+
if !(node.content.nil? or node.content.empty? or node.content =='') and (repeated==1)
|
139
|
+
result << index
|
140
|
+
end
|
141
|
+
end
|
142
|
+
return result
|
143
|
+
end
|
144
|
+
|
145
|
+
# truncate the item completely, deleting all its subitems
|
146
|
+
def truncate
|
147
|
+
subitems.reverse.each{ |subitem| subitem.delete } # reverse je tu jen kvuli performanci, aby to mazal zezadu
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
end
|
data/reinstall_local_gem.sh
CHANGED
data/rspreadsheet.gemspec
CHANGED
@@ -18,8 +18,14 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
+
def self.package_installed?(pkgname)
|
22
|
+
system("dpkg-query -l #{pkgname} | grep -q '^i'")
|
23
|
+
end
|
24
|
+
|
21
25
|
# runtime dependencies
|
22
|
-
|
26
|
+
unless package_installed?('ruby-libxml')
|
27
|
+
spec.add_runtime_dependency 'libxml-ruby', '~>2.7' # parsing XML files
|
28
|
+
end
|
23
29
|
spec.add_runtime_dependency 'rubyzip', '~>1.1' # opening zip files
|
24
30
|
spec.add_runtime_dependency 'andand', '~>1.3'
|
25
31
|
|
@@ -32,8 +38,14 @@ Gem::Specification.new do |spec|
|
|
32
38
|
|
33
39
|
# optional and testing
|
34
40
|
spec.add_development_dependency "coveralls", '~>0.7'
|
35
|
-
|
36
|
-
|
41
|
+
|
42
|
+
if RUBY_VERSION.split('.')[0] != "1"
|
43
|
+
# ruby_dep starts to require ruby 2.2.5 which raises errors with ruby 1.9.3
|
44
|
+
# spec.add_development_dependency "guard", '~>2.13'
|
45
|
+
# spec.add_development_dependency "guard-rspec", '~>4.6'
|
46
|
+
end
|
47
|
+
|
37
48
|
# spec.add_development_dependency 'equivalent-xml' # implementing xml diff
|
38
49
|
|
39
50
|
end
|
51
|
+
|
data/spec/cell_spec.rb
CHANGED
@@ -269,6 +269,21 @@ describe Rspreadsheet::Cell do
|
|
269
269
|
@cell.value.month.should eq 1
|
270
270
|
@cell.value.day.should eq 2
|
271
271
|
end
|
272
|
+
it 'stores time correctly' do
|
273
|
+
@cell = @sheet1.cell(1,1)
|
274
|
+
@cell.value= Time.parse('2:42 pm')
|
275
|
+
@cell.value.hour.should eq 14
|
276
|
+
@cell.value.min.should eq 42
|
277
|
+
@cell.value.sec.should eq 0
|
278
|
+
end
|
279
|
+
it 'can read various types of times', :pending => 'see is' do
|
280
|
+
raise @sheet2.cell('D23').xml.inspect
|
281
|
+
expect {@cell = @sheet2.cell('D22'); @cell.value }.not_to raise_error
|
282
|
+
expect {@cell = @sheet2.cell('D23'); @cell.value }.not_to raise_error
|
283
|
+
|
284
|
+
@cell.value.should == Time.new(2005,5,5,3,33,00)
|
285
|
+
end
|
286
|
+
|
272
287
|
it 'can be addressed by even more ways and all are identical' do
|
273
288
|
@cell = @sheet1.cell(2,2)
|
274
289
|
@sheet1.cell('B2').value = 'zaseste'
|
@@ -292,10 +307,14 @@ describe Rspreadsheet::Cell do
|
|
292
307
|
@cell.format.bold = true
|
293
308
|
@cell.format.bold.should be_truthy
|
294
309
|
@cell.mode.should eq :regular
|
310
|
+
|
311
|
+
@cell = @sheet1.cell(2,2)
|
312
|
+
@cell.format.background_color = '#ffeeaa'
|
313
|
+
@cell.format.background_color.should == '#ffeeaa'
|
314
|
+
@cell.mode.should eq :regular
|
295
315
|
end
|
296
316
|
it 'remembers formula when set' do
|
297
317
|
@cell = @sheet1.cell(1,1)
|
298
|
-
# bold
|
299
318
|
@cell.formula.should be_nil
|
300
319
|
@cell.formula='=1+5'
|
301
320
|
@cell.formula.should eq '=1+5'
|
@@ -327,4 +346,66 @@ describe Rspreadsheet::Cell do
|
|
327
346
|
@czkcell.value.should == 344.to_d
|
328
347
|
@czkcell.format.currency.should == 'CZK'
|
329
348
|
end
|
349
|
+
it 'is possible to manipulate borders of cells' do
|
350
|
+
@cell = @sheet1.cell(1,1)
|
351
|
+
|
352
|
+
[@cell.format.top,@cell.format.left,@cell.format.right,@cell.format.bottom].each do |border|
|
353
|
+
border.style = 'dashed'
|
354
|
+
border.style.should == 'dashed'
|
355
|
+
border.width = 0.5
|
356
|
+
border.width.should == 0.5
|
357
|
+
border.color = '#005500'
|
358
|
+
border.color.should == '#005500'
|
359
|
+
end
|
360
|
+
end
|
361
|
+
it 'returns correct border parameters for the cell' do
|
362
|
+
@sheet2.cell('C8').format.top.style.should == 'solid'
|
363
|
+
@sheet2.cell('E8').format.left.color.should == '#ff3333'
|
364
|
+
@sheet2.cell('E8').format.left.style.should == 'solid'
|
365
|
+
@sheet2.cell('F8').format.top.color.should == '#009900'
|
366
|
+
@sheet2.cell('F8').format.top.style.should == 'dotted'
|
367
|
+
end
|
368
|
+
it 'modifies borders correctly' do
|
369
|
+
## initially solid everywhere
|
370
|
+
@sheet2.cell('C8').format.top.style.should == 'solid'
|
371
|
+
@sheet2.cell('C8').format.bottom.style.should == 'solid'
|
372
|
+
@sheet2.cell('C8').format.left.style.should == 'solid'
|
373
|
+
@sheet2.cell('C8').format.right.style.should == 'solid'
|
374
|
+
## change top and right to dotted and observe
|
375
|
+
@sheet2.cell('C8').format.top.style = 'dotted'
|
376
|
+
@sheet2.cell('C8').format.right.style = 'dotted'
|
377
|
+
@sheet2.cell('C8').format.bottom.style.should == 'solid'
|
378
|
+
@sheet2.cell('C8').format.left.style.should == 'solid'
|
379
|
+
@sheet2.cell('C8').format.top.style.should == 'dotted'
|
380
|
+
@sheet2.cell('C8').format.right.style.should == 'dotted'
|
381
|
+
end
|
382
|
+
it 'deletes borders correctly', :pending=> 'consider how to deal with deleted borders' do
|
383
|
+
@cell = @sheet1.cell(1,1)
|
384
|
+
|
385
|
+
[@cell.format.top,@cell.format.left,@cell.format.right,@cell.format.bottom].each do |border|
|
386
|
+
border.style = 'dashed'
|
387
|
+
border.should_not be_nil
|
388
|
+
border.delete
|
389
|
+
border.should be_nil
|
390
|
+
end
|
391
|
+
|
392
|
+
# delete right border in existing file and observe
|
393
|
+
@sheet2.cell('C8').format.right.delete
|
394
|
+
@sheet2.cell('C8').format.right.should == nil
|
395
|
+
end
|
396
|
+
|
397
|
+
it 'can delete borders in many ways', :pending => 'consider what syntax to support' do
|
398
|
+
@cell=@sheet2.cell('C8')
|
399
|
+
@cell.border_right.should_not be_nil
|
400
|
+
@cell.border_right.delete
|
401
|
+
@cell.border_right.should be_nil
|
402
|
+
|
403
|
+
@cell.border_left.should_not be_nil
|
404
|
+
@cell.border_left = nil
|
405
|
+
@cell.border_left.should be_nil
|
406
|
+
|
407
|
+
@cell.format.top.should_not_be_nil
|
408
|
+
@cell.format.top.style = 'none'
|
409
|
+
@cell.border_top.should_not be_nil ## ?????
|
410
|
+
end
|
330
411
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
if RUBY_VERSION > '2.1'
|
4
|
+
# testing ClassExtensionsForSpec
|
5
|
+
describe LibXML::XML::Node do
|
6
|
+
before do
|
7
|
+
@n = LibXML::XML::Node.new('a')
|
8
|
+
@n << LibXML::XML::Node.new('i','italic')
|
9
|
+
b = LibXML::XML::Node.new('p','paragraph')
|
10
|
+
b << LibXML::XML::Node.new('b','boldtext')
|
11
|
+
@n << b
|
12
|
+
@n << LibXML::XML::Node.new_text('textnode')
|
13
|
+
|
14
|
+
@m = LibXML::XML::Node.new('a')
|
15
|
+
@m << LibXML::XML::Node.new('i','italic')
|
16
|
+
c = LibXML::XML::Node.new('p','paragraph')
|
17
|
+
c << LibXML::XML::Node.new('b','boldtext')
|
18
|
+
@m << c
|
19
|
+
@m << LibXML::XML::Node.new_text('textnode')
|
20
|
+
|
21
|
+
@m2 = LibXML::XML::Node.new('a')
|
22
|
+
end
|
23
|
+
it 'can compare nodes' do
|
24
|
+
@n.should == @m
|
25
|
+
@n.should_not == @m2
|
26
|
+
end
|
27
|
+
it 'has correct elements' do
|
28
|
+
# raise @n.first_diff(@m).inspect
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# testing ClassExtensions
|
33
|
+
begin
|
34
|
+
using ClassExtensions
|
35
|
+
|
36
|
+
describe Array do
|
37
|
+
it 'can sum simple array' do
|
38
|
+
a = [1,2,3,4]
|
39
|
+
a.sum.should == 10
|
40
|
+
end
|
41
|
+
it 'ignores text and nils while summing' do
|
42
|
+
a = [1,nil, nil,2,3,'foo',5.0]
|
43
|
+
a.sum.should == 11
|
44
|
+
[nil, 'nic'].sum.should == 0
|
45
|
+
[].sum.should == 0
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|