rspreadsheet 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ece0fff71891eecf4cbdf45b2c0d305b63a47e37
4
- data.tar.gz: c8429a173fbe6f67d3c870b6cd9220d0a1960ca3
3
+ metadata.gz: 02a08c37680f411ec24be8451e70504c2efcaef5
4
+ data.tar.gz: 1ed4f196aa6891c9a2ba78631a099f03b3e9263f
5
5
  SHA512:
6
- metadata.gz: 84f8ec487137af55e90ec4227e2828248b60b7e943e49cf261e4538971b25e4b9363f325bcb60ddfd9e98790c07ccb3d9c0bd97570dc168ac312cd559b452748
7
- data.tar.gz: 40f34733025850b05f84bd768232bef7f39035733d5973a63d4c24d938e14d9b4021a7a38c7341eab1064535068481e3eaa392b25adec1a3800c19e061cea78c
6
+ metadata.gz: b617514fbf0909eaf9eb3c42fb5794cb8b1182fbf0302c255faa9a6b0ef1aa9a75f6a8489c339f858ef341c605d4ac87d6b5895ca713bcbdeddad346ae395467
7
+ data.tar.gz: 4acb190f8388ecf64d42b6a063ea4b5198803d2d316f7486831f571490dda2c3a001cdd701b8a7cc660e7146d4c448e159cd203e36a77a943aee0965dbb52090
data/.yardopts ADDED
@@ -0,0 +1,5 @@
1
+ --no-private
2
+ --markup markdown
3
+ lib/rspreadsheet/*.rb
4
+ lib/**.rb
5
+
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # Changelog
2
+
3
+ see [Changelog on github](https://github.com/gorn/rspreadsheet/releases)
data/DEVEL_BLOG.md CHANGED
@@ -2,14 +2,9 @@ See [GUIDE.md](GUIDE.md#conventions) for syntax conventions.
2
2
 
3
3
  ## Ideas/wishlist
4
4
 
5
- * What should be returned when asking for row/cell outside used range? Currently it creates new row/cell on fly, but maybe it should only return nil, so the user needs to insert apropriate rows/cells himself before using them. However it spoils little bit syntax like spreadsheet.rows(5).cells(3) because if rows returns nil, that would fail and ugly constructs like spreadsheet.andand.rows(5).andand.cells(3) would be needed. The only way this could be acoided is by using something like "UncreatedRow" object. This concern falls to category "clash of worlds" like the indexing issue (0 vs 1 based) and many others. - now it returns "UncreatedRow/Cell" which is detached upon value assignement.
6
5
  * In future inntroduce syntax like ``sheet.range('C3:E4')`` for mass operations. Also maybe ``sheet.cells('C3')`` or ``sheet.cells(3, 'C')`` etc.
7
6
  * Trying to make row Enumerable - perhaps skipping empty or undefined cells.
8
- * Accessors for nonempty/defined cells.
9
7
  * Maybe insted two syntaxes for accessing cell, we make both of them do the same and return Proxy object which would act either as value or cell.
10
- * Allow any of these:
11
- * ``book['Spring 2014']`` as alternative to ``book.worksheets('Spring 2014')`` ?
12
- * ``sheet.cells('F13')`` as alternative to ``sheet.cells(14,5)`` ?
13
8
  * Document that there is a little distinction betwean RSpreadsheet and RSpreadsheet::Workbook. The former delegates everythink to the other.
14
9
  * allow `book.worskheets.first` syntax
15
10
  * allow `sheet.cells.sum { |cell| cell.value }
@@ -37,22 +32,24 @@ Guiding ideas
37
32
  * [github](https://github.com/gorn/rspreadsheet) hosts the repository where you can get the code
38
33
  * [coverals](https://coveralls.io/r/gorn/rspreadsheet) checks how well source is covered by tests
39
34
 
40
- ### Local manual testing and releasing (to github released, ).
35
+ ### Local testing and releasing (to github and rubygems).
36
+
37
+ 1. make changes
38
+ 2. test if all tests pass (run `bundle exex guard` to test automatically). If not go to 1
39
+ 3. build and install locally
40
+ * ``rake build`` - builds the gem to pkg directory.
41
+ * ``sudo rake install`` - installs gem to local system [^1]
42
+ 4. Now can locally and manually use/test the gem. This should not be replacement for automated testing. If you make some changes, go to 1.
43
+ 5. When happy, increment the version number and `git add .; git commit -am'commit message'; git push`
44
+ 6. ``rake release`` - creates a version tag in git and pushes the code to github + Rubygems. After this is succesfull the new version appears as release in Github and RubyGems.
45
+
46
+ gem alternativa to points 3-6
41
47
 
42
48
  gem build rspreadsheet.gemspec -\ These two lines together are in install.sh
43
49
  sudo gem install rspreadsheet-x.y.z.gem -/ which should be invoked from parent directory
44
50
  gem push rspreadsheet-x.y.z.gem releases the gem, do not forgot to update version in rspreadsheet.gemspec before doing this
45
51
 
46
- alternative way using ``rake`` command - release is more automatic
47
-
48
- 1. build and install locally
49
- * ``rake build`` - builds the gem to pkg directory.
50
- * ``sudo rake install`` - If this fails with "mkmf.rb can't find header files for ruby at /usr/lib/ruby/include/ruby.h" you may want to ``sudo aptitude install ruby-dev``
51
- * Now can locally and manually use/test the gem. This should not be replacement for automated testing. If you make some changes, repeat step 1.
52
- * When happy, increment the version number and deploy in next step.
53
- * ``rake release`` - creates a version tag in git and pushes the code to github + Rubygems. After this is succesfull the new version appears as release in Github and RubyGems.
54
-
55
-
52
+ [^1]: if this fails with "mkmf.rb can't find header files for ruby at /usr/lib/ruby/include/ruby.h" you may want to ``sudo aptitude install ruby-dev``
56
53
 
57
54
  ### Naming conventions
58
55
 
data/Guardfile CHANGED
@@ -1,4 +1,4 @@
1
- guard 'rspec' do
1
+ def watch_all
2
2
  # watch /lib/rspreadsheet/ files
3
3
  watch(%r{^lib/rspreadsheet/(.+).rb$}) do |m|
4
4
  "spec/#{m[1]}_spec.rb"
@@ -8,4 +8,20 @@ guard 'rspec' do
8
8
  watch(%r{^spec/(.+).rb$}) do |m|
9
9
  "spec/#{m[1]}.rb"
10
10
  end
11
- end
11
+ end
12
+
13
+ # classical part
14
+ scope group: :normal
15
+
16
+ group :normal do
17
+ guard 'rspec' do watch_all end
18
+ end
19
+
20
+ # see http://stackoverflow.com/questions/18501471/guard-how-to-run-specific-tags-from-w-in-guards-console
21
+ group :focus do
22
+ guard 'rspec', cli: '--tag focus' do watch_all end
23
+ end
24
+
25
+ #group :f do
26
+ # guard 'rspec', cli: '--tag focus' do watch_all end
27
+ #end
data/README.md CHANGED
@@ -40,7 +40,7 @@ sheet.rows(5).cells.sum{ |cell| cell.value }
40
40
 
41
41
  total = 0
42
42
  sheet.rows.each do |row|
43
- puts "Sponsor #{row[1]} with email #{row(2)} has donated #{row(3)} USD."
43
+ puts "Sponsor #{row[1]} with email #{row[2]} has donated #{row[3]} USD."
44
44
  total += row[3]
45
45
  end
46
46
  puts "Totally fundraised #{total} USD"
@@ -97,3 +97,9 @@ One of the main ideas is that the manipulation with OpenDOcument files should be
97
97
  4. Push to the branch (`git push origin my-new-feature`)
98
98
  5. Create new Pull Request
99
99
 
100
+ ## Further reading
101
+
102
+ * [Advanced Guide](GUIDE.md) how to use the gem
103
+ * [Code documentation](http://www.rubydoc.info/github/gorn/rspreadsheet) is hosted on [rubydoc.info](http://www.rubydoc.info/)
104
+ * [Changelog](CHANGELOG.md)
105
+ * [Documentation for developers](DEVEL_BLOG.md) containing ideas for future development and documentation on testing tools
@@ -1,3 +1,5 @@
1
+ # @private
2
+
1
3
  class LibXML::XML::Node
2
4
  def elements
3
5
  result = []
@@ -34,4 +36,14 @@ class LibXML::XML::Node
34
36
  def equals?(node2) #TODO redefine == with this
35
37
  self.simplification_of?(node2) and node2.simplification_of?(self)
36
38
  end
39
+ end
40
+
41
+ class Array
42
+ def sum(identity = 0, &block)
43
+ if block_given?
44
+ map(&block).sum(identity)
45
+ else
46
+ inject { |sum, element| sum + element } || identity
47
+ end
48
+ end
37
49
  end
data/lib/rspreadsheet.rb CHANGED
@@ -5,6 +5,7 @@ require 'class_extensions'
5
5
 
6
6
  module Rspreadsheet
7
7
 
8
+ # makes creating new workbooks as easy as `Rspreadsheet.new` or `Rspreadsheet.open('filename.ods')
8
9
  def self.new(filename=nil)
9
10
  Workbook.new(filename)
10
11
  end
@@ -1,13 +1,30 @@
1
+ # @markup markdown
2
+ # @author Jakub Tesinsky
3
+ # @titel rspreadsheet Cell
4
+
1
5
  require 'andand'
2
6
  require 'rspreadsheet/xml_tied'
3
7
 
8
+
9
+
4
10
  module Rspreadsheet
5
-
11
+
12
+ ###
13
+ # Represents a cell in spreadsheet which has coordinates, contains value, formula and can be formated.
14
+ # You can get this object like this (suppose that @worksheet contains {Rspreadsheet::Worksheet} object)
15
+ #
16
+ # @worksheet.cells(5,2)
17
+ #
18
+ # Note that when using syntax like `@worksheet[5,2]` or `@worksheet.B5` you won't get this object, but rather the value of the cell.
19
+
6
20
  class Cell < XMLTiedItem
7
21
  attr_accessor :worksheet, :coli, :rowi
8
- def xml_repeated_attribute; 'number-columns-repeated' end
9
- def xml_items_node_name; 'table-cell' end
10
- def xml_options; {:xml_items_node_name => xml_items_node_name, :xml_repeated_attribute => xml_repeated_attribute} end
22
+ # `xml_options[:xml_items_node_name]` gives the name of the tag representing cell
23
+ # `xml_options[:number-columns-repeated]` gives the name of the previous tag which sais how many times the item is repeated
24
+ def xml_options; {:xml_items_node_name => 'table-cell', :xml_repeated_attribute => 'number-columns-repeated'} end
25
+
26
+ ## defining abstract methods from XMLTiedItem
27
+ # returns parent XMLTiedArray object of myself (XMLTiedItem)
11
28
  def parent; row end
12
29
  def index; @coli end
13
30
  def set_index(value); @coli=value end
@@ -59,21 +76,21 @@ class Cell < XMLTiedItem
59
76
  set_type_attribute('float')
60
77
  remove_all_value_attributes_and_content(xmlnode)
61
78
  Tools.set_ns_attribute(xmlnode,'office','value', avalue.to_s)
62
- xmlnode << Tools.create_ns_node('text','p', avalue.to_f.to_s)
79
+ xmlnode << Tools.prepare_ns_node('text','p', avalue.to_f.to_s)
63
80
  when gt == String then
64
81
  set_type_attribute('string')
65
82
  remove_all_value_attributes_and_content(xmlnode)
66
- xmlnode << Tools.create_ns_node('text','p', avalue.to_s)
83
+ xmlnode << Tools.prepare_ns_node('text','p', avalue.to_s)
67
84
  when gt == Date then
68
85
  set_type_attribute('date')
69
86
  remove_all_value_attributes_and_content(xmlnode)
70
87
  Tools.set_ns_attribute(xmlnode,'office','date-value', avalue.strftime('%Y-%m-%d'))
71
- xmlnode << Tools.create_ns_node('text','p', avalue.strftime('%Y-%m-%d'))
88
+ xmlnode << Tools.prepare_ns_node('text','p', avalue.strftime('%Y-%m-%d'))
72
89
  when gt == :percentage
73
90
  set_type_attribute('percentage')
74
91
  remove_all_value_attributes_and_content(xmlnode)
75
92
  Tools.set_ns_attribute(xmlnode,'office','value', '%0.2d%' % avalue.to_f)
76
- xmlnode << Tools.create_ns_node('text','p', (avalue.to_f*100).round.to_s+'%')
93
+ xmlnode << Tools.prepare_ns_node('text','p', (avalue.to_f*100).round.to_s+'%')
77
94
  end
78
95
  else
79
96
  raise "Unknown cell mode #{self.mode}"
@@ -165,22 +182,72 @@ class Cell < XMLTiedItem
165
182
  end
166
183
 
167
184
  # proxy object to allow cell.format syntax. Also handles all logic for formats.
185
+ # @private
168
186
  class CellFormat
169
- attr_reader :bold
170
187
  def initialize(cell)
171
- @bold = false
172
188
  @cell = cell
173
189
  end
174
190
  def cellnode; @cell.xmlnode end
175
- def bold=(value)
176
- Rspreadsheet::Tools.set_ns_attribute(cellnode,'table','style-name','ce99')
177
- end
178
- def bold; Tools.get_ns_attribute_value(text_style_node,'fo','font-weight') == 'bold' end
179
- def italic; Tools.get_ns_attribute_value(text_style_node,'fo','font-style') == 'italic' end
180
- def color; Tools.get_ns_attribute_value(text_style_node,'fo','color') end
181
- def font_size; Tools.get_ns_attribute_value(text_style_node,'fo','font-size') end
182
- def background_color; Tools.get_ns_attribute_value(cell_style_node,'fo','background-color') end
183
-
191
+
192
+ # text style attribute readers
193
+ def bold; get_text_style_node_attribute('font-weight') == 'bold' end
194
+ def italic; get_text_style_node_attribute('font-style') == 'italic' end
195
+ def color; get_text_style_node_attribute('color') end
196
+ def font_size; get_text_style_node_attribute('font-size') end
197
+ def get_text_style_node_attribute(attribute_name)
198
+ text_style_node.nil? ? nil : Tools.get_ns_attribute_value(text_style_node,'fo',attribute_name)
199
+ end
200
+ def background_color; get_cell_style_node_attribute('background-color') end
201
+ def get_cell_style_node_attribute(attribute_name)
202
+ cell_style_node.nil? ? nil : Tools.get_ns_attribute_value(cell_style_node,'fo',attribute_name)
203
+ end
204
+
205
+ # text style attribute writers
206
+ def bold=(value); set_text_style_node_attribute('font-weight', value ? 'bold' : 'normal') end
207
+ def italic=(value); set_text_style_node_attribute('font-style', value ? 'italic' : 'normal') end
208
+ def color=(value); set_text_style_node_attribute('color', value) end
209
+ def font_size=(value);set_text_style_node_attribute('font-size', value) end
210
+ def set_text_style_node_attribute(attribute_name,value)
211
+ if text_style_node.nil?
212
+ self.create_text_style_node
213
+ raise 'Style node was not correctly initialized' if text_style_node.nil?
214
+ end
215
+ Tools.set_ns_attribute(text_style_node,'fo',attribute_name,value)
216
+ end
217
+ def background_color=(value); set_cell_style_node_attribute('background-color', value) end
218
+ def set_cell_style_node_attribute(attribute_name,value)
219
+ if cell_style_node.nil?
220
+ self.create_cell_style_node
221
+ raise 'Style node was not correctly initialized' if cell_style_node.nil?
222
+ end
223
+ Tools.set_ns_attribute(cell_style_node,'fo',attribute_name,value)
224
+ end
225
+
226
+ # @!group initialization of style related nodes, if they do not exist
227
+ def create_text_style_node
228
+ create_style_node if style_name.nil? or style_node.nil?
229
+ raise 'text_style_node already exists' unless text_style_node.nil?
230
+ style_node << Tools.prepare_ns_node('style','text-properties')
231
+ end
232
+ def create_cell_style_node
233
+ create_style_node if style_name.nil? or style_node.nil?
234
+ raise 'cell_style_node already exists' unless cell_style_node.nil?
235
+ style_node << Tools.prepare_ns_node('style','table-cell-properties')
236
+ end
237
+ def create_style_node
238
+ if style_name.nil?
239
+ proposed_style_name = unused_cell_style_name
240
+ Tools.set_ns_attribute(cellnode,'table','style-name',proposed_style_name)
241
+ raise 'Style name was not correctly initialized' if style_name!=proposed_style_name
242
+ end
243
+ anode = Tools.prepare_ns_node('style','style')
244
+ Tools.set_ns_attribute(anode, 'style', 'name', proposed_style_name)
245
+ Tools.set_ns_attribute(anode, 'style', 'family', 'table-cell')
246
+ Tools.set_ns_attribute(anode, 'style', 'parent-style-name', 'Default')
247
+ automatic_styles_node << anode
248
+ raise 'Style node was not correctly initialized' if style_node.nil?
249
+ end
250
+
184
251
  def unused_cell_style_name
185
252
  last = cellnode.doc.root.find('./office:automatic-styles/style:style').
186
253
  collect {|node| node['name']}.
@@ -188,6 +255,7 @@ class CellFormat
188
255
  compact.max
189
256
  "ce#{last+1}"
190
257
  end
258
+ def automatic_styles_node; cellnode.doc.root.find("./office:automatic-styles").first end
191
259
  def style_name; Tools.get_ns_attribute_value(cellnode,'table','style-name') end
192
260
  def style_node; cellnode.doc.root.find("./office:automatic-styles/style:style[@style:name=\"#{style_name}\"]").first end
193
261
  def text_style_node; cellnode.doc.root.find("./office:automatic-styles/style:style[@style:name=\"#{style_name}\"]/style:text-properties").first end
@@ -1,45 +1,57 @@
1
1
  require 'rspreadsheet/cell'
2
2
  require 'rspreadsheet/xml_tied'
3
3
 
4
- # Currently this is only syntax sugar for cells and contains no functionality
5
4
 
6
5
  module Rspreadsheet
7
6
 
7
+ # Represents a row in a spreadsheet which has coordinates, contains value, formula and can be formated.
8
+ # You can get this object like this (suppose that @worksheet contains {Rspreadsheet::Worksheet} object)
9
+ #
10
+ # @row = @worksheet.rows(5)
11
+ #
12
+ # Mostly you will this object to access row cells
13
+ #
14
+ # @row.cells(2) # identical to @worksheet.rows(5).cells(2)
15
+ # @row[2] # identical to @worksheet[5,2] or @row.cells(2)
16
+ #
17
+ # or manipulate rows
18
+ #
19
+ # @row.add_row_above # adds empty row above
20
+ # @row.delete # deletes row
21
+ #
22
+ # and shifts all other rows down/up appropriatelly.
23
+
8
24
  class Row < XMLTiedItem
9
25
  include XMLTiedArray
10
- attr_reader :worksheet, :rowi
11
- def xml_repeated_attribute; 'number-rows-repeated' end
12
- def xml_items_node_name; 'table-row' end
13
- def xml_options; {:xml_items_node_name => xml_items_node_name, :xml_repeated_attribute => xml_repeated_attribute} end
14
- def subitem_xml_options; {:xml_items_node_name => 'table-cell', :xml_repeated_attribute => 'number-columns-repeated'} end
26
+ ## @return [Worksheet] worksheet which contains the row
27
+ # @!attribute [r] worksheet
28
+ attr_reader :worksheet
29
+ ## @return [Integer] row index of the row
30
+ # @!attribute [r] rowi
31
+ attr_reader :rowi
15
32
 
16
33
  def initialize(aworksheet,arowi)
17
34
  @worksheet = aworksheet
18
35
  @rowi = arowi
19
36
  @itemcache = Hash.new #TODO: move to module XMLTiedArray
20
37
  end
38
+
21
39
  def xmlnode; parent.find_my_subnode_respect_repeated(index, xml_options) end
22
40
 
23
- # XMLTiedItem things and extensions
24
- def parent; @worksheet end
25
- def index; @rowi end
26
- def set_index(value); @rowi=value end
27
-
28
- # XMLTiedArray rozšíření
29
- def prepare_subitem(coli); Cell.new(@worksheet,@rowi,coli) end
30
- def cells(*params)
31
- raise 'Invalid row reference' if invalid_reference?
32
- case params.length
33
- when 0 then subitems_array
34
- when 1 then subitem(params[0])
35
- else raise Exception.new('Wrong number of arguments.')
36
- end
37
- end
38
- # syntactis sugar to cells and its values
41
+ # @!group Syntactic sugar
42
+ def cells(*params); subitems(*params) end
43
+
44
+ ## @return [String or Float or Date] value of the cell
45
+ # @param coli [Integer] colum index of the cell
46
+ # returns value of the cell at column `coli`
39
47
  def [](coli); cells(coli).value end
48
+ ## @param coli [Integer] colum index of the cell
49
+ # @param avalue [String or Float or Date] colum index of the cell
50
+ # sets value of the cell at column `coli`
40
51
  def []=(coli,avalue); cells(coli).value=avalue end
41
-
42
- # další
52
+ # @!endgroup
53
+
54
+ # @!group Other methods
43
55
  def style_name=(value);
44
56
  detach_if_needed
45
57
  Tools.set_ns_attribute(xmlnode,'table','style-name',value)
@@ -56,10 +68,32 @@ class Row < XMLTiedItem
56
68
  end
57
69
  end
58
70
  alias :used_range :range
59
- def shift_by(diff)
71
+ # Inserts row above itself (and shifts itself and all following rows down)
72
+ def add_row_above
73
+ parent.add_row_above(rowi)
74
+ end
75
+ def cellvalues
76
+ cells.collect{|c| c.value}
77
+ end
78
+
79
+ # @!group Private methods, which should not be called directly
80
+ # @private
81
+ # shifts internal represetation of row by diff. This should not be called directly
82
+ # by user, it is only used by XMLTiedArray as hook when shifting around rows
83
+ def _shift_by(diff)
60
84
  super
61
85
  @itemcache.each_value{ |cell| cell.set_rowi(@rowi) }
62
86
  end
87
+
88
+ private
89
+ # @!group XMLTiedArray related methods
90
+ def subitem_xml_options; {:xml_items_node_name => 'table-cell', :xml_repeated_attribute => 'number-columns-repeated'} end
91
+ def prepare_subitem(coli); Cell.new(@worksheet,@rowi,coli) end
92
+ # @!group XMLTiedItem related methods and extensions
93
+ def xml_options; {:xml_items_node_name => 'table-row', :xml_repeated_attribute => 'number-rows-repeated'} end
94
+ def parent; @worksheet end
95
+ def index; @rowi end
96
+ def set_index(value); @rowi=value end
63
97
  end
64
98
 
65
99
  # class Row
@@ -2,31 +2,59 @@ module Rspreadsheet
2
2
 
3
3
  # this module contains methods used bz several objects
4
4
  module Tools
5
+ def self.only_letters?(x); x.kind_of?(String) and x.match(/^[A-Za-z]*$/) != nil end
6
+ def self.kind_of_integer?(x)
7
+ (x.kind_of?(Numeric) and x.to_i==x) or
8
+ (x.kind_of?(String) and x.match(/^\d*(\.0*)?$/) != nil)
9
+ end
10
+
5
11
  # converts cell adress like 'F12' to pair od integers [row,col]
6
12
  def self.convert_cell_address_to_coordinates(*addr)
7
13
  if addr.length == 1
8
- addr[0].match(/^([A-Z]{1,3})(\d{1,8})$/)
14
+ addr[0].match(/^([A-Za-z]{1,3})(\d{1,8})$/)
9
15
  colname = $~[1]
10
- rowname = $~[2]
16
+ rowname = $~[2].to_i
11
17
  elsif addr.length == 2
12
- colname = addr[0]
13
- rowname = addr[1]
18
+ a = addr[0]; b = addr[1]
19
+ if a.kind_of?(Integer) and b.kind_of?(Integer) # most common case first
20
+ colname,rowname = b,a
21
+ elsif only_letters?(a)
22
+ if kind_of_integer?(b)
23
+ colname,rowname = a,b.to_i
24
+ else
25
+ raise 'Wrong parameters - first is letters, but the seconds is not digits only'
26
+ end
27
+ elsif kind_of_integer?(a)
28
+ if only_letters?(b)
29
+ colname,rowname = b,a.to_i
30
+ elsif kind_of_integer?(b)
31
+ colname,rowname = b.to_i,a.to_i
32
+ else
33
+ raise 'Wrong second out of two paremeters - mix of digits and numbers'
34
+ end
35
+ else
36
+ raise 'Wrong first out of two paremeters - mix of digits and numbers'
37
+ end
14
38
  else
15
39
  raise 'Wrong number of arguments'
16
40
  end
17
41
 
18
42
  ## first possibility how to implement it
19
- # colname=colname.rjust(3,'@')
20
- # col = (colname[-1].ord-64)+(colname[-2].ord-64)*26+(colname[-3].ord-64)*26*26
43
+ # colname=colname.rjust(3,'@')
44
+ # col = (colname[-1].ord-64)+(colname[-2].ord-64)*26+(colname[-3].ord-64)*26*26
21
45
 
22
46
  ## second possibility how to implement it
23
47
  # col=(colname.to_i(36)-('A'*colname.size).to_i(36)).to_s(36).to_i(26)+('1'*colname.size).to_i(26)
24
48
 
49
+ if colname.kind_of?(Integer)
50
+ col=colname
51
+ else
25
52
  ## third possibility how to implement it (second one little shortened)
26
- s=colname.size
27
- col=(colname.to_i(36)-(36**s-1).div(3.5)).to_s(36).to_i(26)+(26**s-1)/25
53
+ s=colname.size
54
+ col=(colname.upcase.to_i(36)-(36**s-1).div(3.5)).to_s(36).to_i(26)+(26**s-1)/25
55
+ end
28
56
 
29
- row = rowname.to_i
57
+ row = rowname
30
58
  return [row,col]
31
59
  end
32
60
  def self.convert_cell_coordinates_to_address(*coords)
@@ -50,6 +78,9 @@ module Tools
50
78
  end
51
79
  def self.c2a(*x); convert_cell_coordinates_to_address(*x) end
52
80
  def self.a2c(*x); convert_cell_address_to_coordinates(*x) end
81
+ # Finds {LibXML::XML::Namespace} object by its prefix. It knows all OpenDocument commonly used namespaces.
82
+ # @return [LibXML::XML::Namespace] namespace object
83
+ # @param prefix [String] namespace prefix
53
84
  def self.get_namespace(prefix)
54
85
  ns_array = {
55
86
  'office'=>"urn:oasis:names:tc:opendocument:xmlns:office:1.0",
@@ -114,8 +145,12 @@ module Tools
114
145
  nil
115
146
  end
116
147
  end
117
- def self.get_ns_attribute(node,ns_prefix,key)
118
- node.nil? ? nil : node.attributes.get_attribute_ns(Tools.get_namespace(ns_prefix).href,key)
148
+ def self.get_ns_attribute(node,ns_prefix,key,default=:undefined_default)
149
+ if default==:undefined_default
150
+ node.attributes.get_attribute_ns(Tools.get_namespace(ns_prefix).href,key)
151
+ else
152
+ node.nil? ? default : node.attributes.get_attribute_ns(Tools.get_namespace(ns_prefix).href,key) || default
153
+ end
119
154
  end
120
155
  def self.get_ns_attribute_value(node,ns_prefix,key)
121
156
  Tools.get_ns_attribute(node,ns_prefix,key).andand.value
@@ -124,13 +159,14 @@ module Tools
124
159
  node.attributes.get_attribute_ns(Tools.get_namespace(ns_prefix).href,key)
125
160
  attr.remove! unless attr.nil?
126
161
  end
127
- def self.create_ns_node(ns_prefix,nodename,value=nil)
162
+ def self.prepare_ns_node(ns_prefix,nodename,value=nil)
128
163
  LibXML::XML::Node.new(nodename,value, Tools.get_namespace(ns_prefix))
129
164
  end
130
165
  end
131
166
 
132
167
  end
133
168
 
169
+ # @private
134
170
  class Range
135
171
  def size
136
172
  res = self.end-self.begin+1
@@ -1,3 +1,3 @@
1
1
  module Rspreadsheet
2
- VERSION = "0.2.3"
2
+ VERSION = "0.2.4"
3
3
  end
@@ -3,10 +3,10 @@ require 'libxml'
3
3
 
4
4
  module Rspreadsheet
5
5
  class Workbook
6
- attr_reader :worksheets, :filename
6
+ attr_reader :filename
7
7
  attr_reader :xmlnode # debug
8
8
  def initialize(afilename=nil)
9
- @worksheets={}
9
+ @worksheets=[]
10
10
  @filename = afilename
11
11
  @content_xml = Zip::File.open(@filename || './lib/rspreadsheet/empty_file_template.ods') do |zip|
12
12
  LibXML::XML::Document.io zip.get_input_stream('content.xml')
@@ -30,34 +30,46 @@ class Workbook
30
30
  end
31
31
  end
32
32
  end
33
+ def xmldoc; @xmlnode.doc end
34
+
35
+ #@!group Worskheets methods
33
36
  def create_worksheet_from_node(source_node)
34
37
  sheet = Worksheet.new(source_node)
35
38
  register_worksheet(sheet)
36
39
  return sheet
37
40
  end
38
- def create_worksheet_with_name(name)
41
+ def create_worksheet(name = "Strana #{worksheets_count}")
39
42
  sheet = Worksheet.new(name)
40
43
  register_worksheet(sheet)
41
44
  return sheet
42
45
  end
43
- def create_worksheet
44
- index = worksheets_count
45
- create_worksheet_with_name("Strana #{index}")
46
+ # @return [Integer] number of sheets in the workbook
47
+ def worksheets_count; @worksheets.length end
48
+ # @return [String] names of sheets in the workbook
49
+ def worksheet_names; @worksheets.collect{ |ws| ws.name } end
50
+ # @param [Integer,String]
51
+ # @return [Worskheet] worksheet with given index or name
52
+ def worksheets(index_or_name)
53
+ case index_or_name
54
+ when Integer then begin
55
+ case index_or_name
56
+ when 0 then nil
57
+ when 1..Float::INFINITY then @worksheets[index_or_name-1]
58
+ when -Float::INFINITY..-1 then @worksheets[index_or_name] # zaporne indexy znamenaji pocitani zezadu
59
+ end
60
+ end
61
+ when String then @worksheets.select{|ws| ws.name == index_or_name}.first
62
+ when NilClass then nil
63
+ else raise 'method worksheets requires Integer index of the sheet or its String name'
64
+ end
46
65
  end
66
+ def [](index_or_name); self.worksheets(index_or_name) end
67
+
68
+ private
47
69
  def register_worksheet(worksheet)
48
70
  index = worksheets_count+1
49
- @worksheets[index]=worksheet
50
- @worksheets[worksheet.name]=worksheet unless worksheet.name.nil?
71
+ @worksheets[index-1]=worksheet
51
72
  @xmlnode << worksheet.xmlnode if worksheet.xmlnode.doc != @xmlnode.doc
52
73
  end
53
- def worksheets_count
54
- @worksheets.keys.select{ |k| k.kind_of? Numeric }.size #TODO: ?? max
55
- end
56
- def worksheet_names
57
- @worksheets.keys.reject{ |k| k.kind_of? Numeric }
58
- end
59
- def xmldoc
60
- @xmlnode.doc
61
- end
62
74
  end
63
75
  end
@@ -6,7 +6,7 @@ module Rspreadsheet
6
6
 
7
7
  class Worksheet
8
8
  include XMLTiedArray
9
- attr_accessor :name, :xmlnode
9
+ attr_accessor :xmlnode
10
10
  def subitem_xml_options; {:xml_items_node_name => 'table-row', :xml_repeated_attribute => 'number-rows-repeated'} end
11
11
 
12
12
  def initialize(xmlnode_or_sheet_name)
@@ -16,12 +16,17 @@ class Worksheet
16
16
  when LibXML::XML::Node
17
17
  @xmlnode = xmlnode_or_sheet_name
18
18
  when String
19
- @xmlnode = Tools.create_ns_node('table','table')
20
- Tools.set_ns_attribute(@xmlnode,'table','name', xmlnode_or_sheet_name)
19
+ @xmlnode = Tools.prepare_ns_node('table','table')
20
+ self.name = xmlnode_or_sheet_name
21
21
  else raise 'Provide name or xml node to create a Worksheet object'
22
22
  end
23
23
  end
24
24
 
25
+ # name of the worksheet
26
+ # @returns [String]
27
+ def name; Tools.get_ns_attribute_value(@xmlnode,'table','name') end
28
+ def name=(value); Tools.set_ns_attribute(@xmlnode,'table','name', value) end
29
+
25
30
  def rowxmlnode(rowi)
26
31
  find_my_subnode_respect_repeated(rowi, {:xml_items_node_name => 'table-row', :xml_repeated_attribute => 'number-rows-repeated'})
27
32
  end
@@ -30,13 +35,13 @@ class Worksheet
30
35
  find_first_unused_index_respect_repeated({:xml_items_node_name => 'table-row', :xml_repeated_attribute => 'number-rows-repeated'})
31
36
  end
32
37
 
33
- def insert_row_above(arowi)
34
- insert_subitem_before(arowi)
38
+ def add_row_above(arowi)
39
+ add_empty_subitem_before(arowi)
35
40
  end
36
41
 
37
42
  def insert_cell_before(arowi,acoli)
38
43
  detach_row_in_xml(arowi)
39
- rows(arowi).insert_subitem_before(acoli)
44
+ rows(arowi).add_empty_subitem_before(acoli)
40
45
  end
41
46
 
42
47
  def detach_row_in_xml(rowi)
@@ -47,22 +52,33 @@ class Worksheet
47
52
  used_rows_range.collect{ |rowi| rows(rowi).nonemptycells }.flatten
48
53
  end
49
54
 
50
- # rozšíření XMLTiedArray
51
- def rows(rowi); subitem(rowi) end
55
+ #@!group XMLTiedArray connected methods
56
+ def rows(*params); subitems(*params) end
52
57
  def prepare_subitem(rowi); Row.new(self,rowi) end
53
58
  def rowcache; @itemcache end
54
59
 
55
- ## syntactic sugar follows
56
- def [](r,c)
57
- cells(r,c).andand.value
60
+ #@!group How to get to cells? (syntactic sugar)
61
+ # Returns value of the cell given either by row,column integer coordinates of by address.
62
+ # @param [(Integer,Integer), String] either row and column of the cell (i.e. 3,5) or a string containing it address i.e. 'F12'
63
+ def [](*params)
64
+ cells(*params).andand.value
58
65
  end
59
- def []=(r,c,avalue)
60
- cells(r,c).andand.value=avalue
66
+ # Aets value of the cell given either by row,column integer coordinates of by address. It also sets the type of the cell according to type of the value. For details #see Cell.value=
67
+ def []=(*params)
68
+ cells(*params[0..-2]).andand.value = params.last
61
69
  end
62
- def cells(r,c)
63
- rows(r).andand.cells(c)
70
+ # Returns a Cell object placed in row and column or on a Cell on string address
71
+ # @param [(Integer,Integer), String] either row and column of the cell (i.e. 3,5) or a string containing it address i.e. 'F12'
72
+ def cells(*params)
73
+ case params.length
74
+ when 0 then raise 'Not implemented yet' #TODO: return list of all cells
75
+ when 1..2
76
+ r,c = Rspreadsheet::Tools.a2c(*params)
77
+ rows(r).andand.cells(c)
78
+ else raise Exception.new('Wrong number of arguments.')
79
+ end
64
80
  end
65
- # allows syntax like sheet.F15
81
+ # 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.
66
82
  def method_missing method_name, *args, &block
67
83
  if method_name.to_s.match(/^([A-Z]{1,3})(\d{1,8})(=?)$/)
68
84
  row,col = Rspreadsheet::Tools.convert_cell_address_to_coordinates($~[1],$~[2])
@@ -1,11 +1,13 @@
1
1
  module Rspreadsheet
2
2
 
3
+ # @private
3
4
  class XMLTied
4
5
  def xml
5
6
  xmlnode.to_s
6
7
  end
7
8
  end
8
9
 
10
+ # @private
9
11
  # abstrac class. All successort MUST implement: set_index,xml_options,parent,index
10
12
  class XMLTiedItem < XMLTied
11
13
  def mode
@@ -15,7 +17,7 @@ class XMLTiedItem < XMLTied
15
17
  else :regular
16
18
  end
17
19
  end
18
- def repeated; (Tools.get_ns_attribute_value(xmlnode, 'table', xml_repeated_attribute) || 1 ).to_i end
20
+ def repeated; (Tools.get_ns_attribute_value(xmlnode, 'table', xml_options[:xml_repeated_attribute]) || 1 ).to_i end
19
21
  def repeated?; mode==:repeated || mode==:outbound end
20
22
  alias :is_repeated? :repeated?
21
23
  def xmlnode
@@ -34,7 +36,7 @@ class XMLTiedItem < XMLTied
34
36
  parent.detach_my_subnode_respect_repeated(index, xml_options)
35
37
  self
36
38
  end
37
- def shift_by(diff)
39
+ def _shift_by(diff)
38
40
  set_index(index + diff)
39
41
  end
40
42
  def range
@@ -59,12 +61,14 @@ class XMLTiedItem < XMLTied
59
61
  parent.delete_subitem(index)
60
62
  invalidate_myself
61
63
  end
64
+
62
65
  end
63
66
 
64
67
  # abstrac class. All successort MUST implement: prepare_subitem
65
68
  # terminology
66
69
  # item, subitem is object from @itemcache (quite often subclass of XMLTiedItem)
67
70
  # node, subnode is LibXML::XML::Node object
71
+ # @private
68
72
 
69
73
  module XMLTiedArray
70
74
  attr_reader :itemcache
@@ -96,6 +100,14 @@ module XMLTiedArray
96
100
  end
97
101
  end
98
102
 
103
+ def subitems(*params)
104
+ case params.length
105
+ when 0 then subitems_array
106
+ when 1 then subitem(params[0])
107
+ else raise Exception.new('Wrong number of arguments.')
108
+ end
109
+ end
110
+
99
111
  def subitems_array
100
112
  (1..(find_first_unused_index_respect_repeated(subitem_xml_options)-1)).collect do |i|
101
113
  subitem(i)
@@ -184,15 +196,12 @@ module XMLTiedArray
184
196
  return index+1
185
197
  end
186
198
 
187
- def insert_subitem_before(aindex)
188
- insert_subitem_before_with_options(aindex,subitem_xml_options)
189
- end
190
- def insert_subitem_before_with_options(aindex,options)
199
+ def add_empty_subitem_before(aindex)
191
200
  @itemcache.keys.sort.reverse.select{|i| i>=aindex }.each do |i|
192
201
  @itemcache[i+1]=@itemcache.delete(i)
193
- @itemcache[i+1].shift_by(1)
202
+ @itemcache[i+1]._shift_by(1)
194
203
  end
195
- insert_my_subnode_before_respect_repeated(aindex,options) # nyní vlož node do xml
204
+ insert_my_subnode_before_respect_repeated(aindex,subitem_xml_options) # nyní vlož node do xml
196
205
  @itemcache[aindex] = subitem(aindex)
197
206
  end
198
207
 
@@ -204,7 +213,7 @@ module XMLTiedArray
204
213
  @itemcache.delete(aindex)
205
214
  @itemcache.keys.sort.select{|i| i>=aindex+1 }.each do |i|
206
215
  @itemcache[i-1]=@itemcache.delete(i)
207
- @itemcache[i-1].shift_by(-1)
216
+ @itemcache[i-1]._shift_by(-1)
208
217
  end
209
218
  end
210
219
 
data/spec/cell_spec.rb CHANGED
@@ -4,9 +4,8 @@ describe Rspreadsheet::Cell do
4
4
  before do
5
5
  book1 = Rspreadsheet.new
6
6
  @sheet1 = book1.create_worksheet
7
- @c = @sheet1.cells(1,1)
8
7
  book2 = Rspreadsheet.new($test_filename)
9
- @sheet2 = book2.worksheets[1]
8
+ @sheet2 = book2.worksheets(1)
10
9
  end
11
10
  it 'contains good row and col coordinates' do
12
11
  @cell = @sheet1.cells(1,3)
@@ -186,8 +185,6 @@ describe Rspreadsheet::Cell do
186
185
  @cell.xmlnode.attributes['style-name'].should_not be_nil
187
186
  end
188
187
  it 'can set formats of the cells' do
189
- skip 'not implemented yet'; pending
190
- =begin
191
188
  @cell = @sheet2.cells(1,1)
192
189
  # bold
193
190
  @cell.format.bold.should be_falsey
@@ -204,12 +201,12 @@ describe Rspreadsheet::Cell do
204
201
  # background_color
205
202
  @cell.format.background_color.should be_nil
206
203
  @cell.format.background_color = '#AABBCC'
204
+ @cell.format.style_name.should_not eq 'cell'
207
205
  @cell.format.background_color.should eq '#AABBCC'
208
206
  # font_size
209
207
  @cell.format.font_size.should be_nil
210
208
  @cell.format.font_size = '11pt'
211
209
  @cell.format.font_size.should eq '11pt'
212
- =end
213
210
  end
214
211
  it 'method cells without arguments returns array of cells' do
215
212
  @a = @sheet2.rows(1).cells
@@ -221,7 +218,7 @@ describe Rspreadsheet::Cell do
221
218
  @sheet1.cells(2,2).detach
222
219
  @cell = @sheet1.cells(2,2)
223
220
  @cell.rowi.should == 2
224
- @sheet1.insert_row_above(1)
221
+ @sheet1.add_row_above(1)
225
222
  @cell.rowi.should == 3
226
223
  end
227
224
  it 'switches to invalid_reference cell when deleted' do
@@ -251,10 +248,27 @@ describe Rspreadsheet::Cell do
251
248
  expect(@cell.inspect).to include('abcde','::Cell','6','2','row')
252
249
  end
253
250
  it 'stores date correctly' do
254
- @c.value= Date.parse('2014-01-02')
255
- @c.value.year.should eq 2014
256
- @c.value.month.should eq 1
257
- @c.value.day.should eq 2
251
+ @cell = @sheet1.cells(1,1)
252
+ @cell.value= Date.parse('2014-01-02')
253
+ @cell.value.year.should eq 2014
254
+ @cell.value.month.should eq 1
255
+ @cell.value.day.should eq 2
256
+ end
257
+ it 'can be addressed by even more ways and all are identical' do
258
+ @cell = @sheet1.cells(2,2)
259
+ @sheet1.cells('B2').value = 'zaseste'
260
+ @sheet1.cells('B2').value.should == 'zaseste'
261
+ @cell.value.should == 'zaseste'
262
+ @sheet1.cells(2,'B').value.should == 'zaseste'
263
+ @sheet1.cells(2,'B').value = 'zasedme'
264
+ @cell.value.should == 'zasedme'
265
+ @sheet1['B2'].should == 'zasedme'
266
+ @sheet1['B2'] = 'zaosme'
267
+ @cell.value.should == 'zaosme'
268
+
269
+ @sheet2.cells('F2').should be @sheet2.cells(2,6)
270
+ @sheet2.cells('BA177').should be @sheet2.cells(177,53)
271
+ @sheet2.cells('ADA2').should be @sheet2.cells(2,781)
258
272
  end
259
273
  end
260
274
 
data/spec/row_spec.rb CHANGED
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Rspreadsheet::Row do
4
4
  before do
5
5
  @sheet1 = Rspreadsheet.new.create_worksheet
6
- @sheet2 = Rspreadsheet.new($test_filename).worksheets[1]
6
+ @sheet2 = Rspreadsheet.new($test_filename).worksheets(1)
7
7
  end
8
8
  it 'allows access to cells in a row' do
9
9
  @row = @sheet2.rows(1)
@@ -106,7 +106,7 @@ describe Rspreadsheet::Row do
106
106
  end
107
107
  it 'can open ods testfile and read its content' do
108
108
  book = Rspreadsheet.new($test_filename)
109
- s = book.worksheets[1]
109
+ s = book.worksheets(1)
110
110
  (1..10).each do |i|
111
111
  s.rows(i).should be_kind_of(Rspreadsheet::Row)
112
112
  s.rows(i).repeated.should == 1
@@ -168,14 +168,17 @@ describe Rspreadsheet::Row do
168
168
  @row.range.should == (16..19)
169
169
  @row.rowi.should == 16
170
170
 
171
- @sheet1.insert_row_above(7)
171
+ @sheet1.add_row_above(7)
172
172
  @sheet1.rows(17).range.should == (17..20)
173
173
  @row.range.should == (17..20)
174
174
  @row.rowi.should == 17
175
- @sheet1.rows(17).should equal(@row)
175
+ @sheet1.rows(17).should equal(@row)
176
+
177
+ @row.add_row_above
178
+ @row.rowi.should == 18
176
179
  end
177
180
  it 'inserted has correct class' do # based on real error
178
- @sheet2.insert_row_above(1)
181
+ @sheet2.add_row_above(1)
179
182
  @sheet2.rows(1).should be_kind_of(Rspreadsheet::Row)
180
183
  end
181
184
  it 'can be deleted' do
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe Rspreadsheet do
4
4
  it 'can open ods testfile and reads its content correctly' do
5
5
  book = Rspreadsheet.new($test_filename)
6
- s = book.worksheets[1]
6
+ s = book.worksheets(1)
7
7
  (1..10).each do |i|
8
8
  s[i,1].should === i
9
9
  end
@@ -18,8 +18,8 @@ describe Rspreadsheet do
18
18
 
19
19
  book1 = Rspreadsheet.new($test_filename) # now open both again
20
20
  book2 = Rspreadsheet.new(tmp_filename)
21
- @sheet1 = book1.worksheets[1]
22
- @sheet2 = book2.worksheets[1]
21
+ @sheet1 = book1.worksheets(1)
22
+ @sheet2 = book2.worksheets(1)
23
23
 
24
24
  @sheet1.nonemptycells.each do |cell| # and test identity
25
25
  @sheet2[cell.rowi,cell.coli].should == cell.value
@@ -48,9 +48,9 @@ describe Rspreadsheet do
48
48
  tmp_filename = '/tmp/testfile1.ods' # first delete temp file
49
49
  File.delete(tmp_filename) if File.exists?(tmp_filename)
50
50
  book = Rspreadsheet.new($test_filename) # than open test file
51
- book.worksheets[1].rows(1).cells(1).value.should_not == 'xyzxyz'
52
- book.worksheets[1].rows(1).cells(1).value ='xyzxyz'
53
- book.worksheets[1].rows(1).cells(1).value.should == 'xyzxyz'
51
+ book.worksheets(1).rows(1).cells(1).value.should_not == 'xyzxyz'
52
+ book.worksheets(1).rows(1).cells(1).value ='xyzxyz'
53
+ book.worksheets(1).rows(1).cells(1).value.should == 'xyzxyz'
54
54
 
55
55
  book.save(tmp_filename) # and save it as temp file
56
56
 
@@ -71,11 +71,8 @@ describe Rspreadsheet do
71
71
  book.create_worksheet
72
72
  end
73
73
  it 'examples from README file are working' do
74
- skip 'not implemented yet'; pending
75
- =begin
76
-
77
74
  book = Rspreadsheet.open($test_filename)
78
- sheet = book.worksheets[1]
75
+ sheet = book.worksheets(1)
79
76
  sheet.B5 = 'cell value'
80
77
 
81
78
  sheet.B5.should eq 'cell value'
@@ -91,16 +88,15 @@ describe Rspreadsheet do
91
88
  sheet.cells(5,2).format.background_color = '#FF0000'
92
89
  }.not_to raise_error
93
90
 
94
- sheet.rows(4).cellvalues.sum.should eq 4+7*4
95
- sheet.rows(5).cells.sum{ |cell| cell.value }.should eq 4+7*4
91
+ sheet.rows(4).cellvalues.sum{|val| val.to_f}.should eq 4+7*4
92
+ sheet.rows(4).cells.sum{ |cell| cell.value.to_f }.should eq 4+7*4
96
93
 
97
94
  total = 0
98
95
  sheet.rows.each do |row|
99
- expect {"Sponsor #{row[1]} with email #{row(2)} has donated #{row(3)} USD." }.not_to raise_error
100
- total += row[1]
96
+ expect {"Sponsor #{row[1]} with email #{row[2]} has donated #{row[3]} USD." }.not_to raise_error
97
+ total += row[1].to_f
101
98
  end
102
- total.should eq 99
103
- =end
99
+ total.should eq 55
104
100
  end
105
101
  end
106
102
 
data/spec/spec_helper.rb CHANGED
@@ -1,11 +1,20 @@
1
+
2
+ # This tells Bundler only load gems in side gemspec (not the locally installed ones). See http://stackoverflow.com/questions/4398262/setup-rspec-to-test-a-gem-not-rails
3
+ require 'bundler/setup'
4
+ Bundler.setup
5
+
1
6
  RSpec.configure do |c|
2
7
  c.fail_fast = true
3
8
  # c.warnings = true
9
+ c.treat_symbols_as_metadata_keys_with_true_values = true # so i can run individual test just by appending :focus to them
4
10
  end
5
11
 
12
+ # this enables Coveralls
6
13
  require 'coveralls'
7
14
  Coveralls.wear!
8
15
 
16
+ # some variables used everywhere
9
17
  $test_filename = './spec/testfile1.ods'
10
18
 
19
+ # require my gem
11
20
  require 'rspreadsheet'
data/spec/tools_spec.rb CHANGED
@@ -1,25 +1,49 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Rspreadsheet::Tools do
4
- it 'Converts correctly cell adresses to coordinates and back' do
5
- Rspreadsheet::Tools.convert_cell_address_to_coordinates('A1').should == [1,1]
6
- Rspreadsheet::Tools.convert_cell_address_to_coordinates('C17').should == [17,3]
7
- Rspreadsheet::Tools.convert_cell_address_to_coordinates('AM1048576').should == [1048576,39]
8
- Rspreadsheet::Tools.convert_cell_address_to_coordinates('ADA2').should == [2,781]
9
- Rspreadsheet::Tools.convert_cell_address_to_coordinates('ZZ1').should == [1,702]
10
- Rspreadsheet::Tools.convert_cell_coordinates_to_address([1,1]).should == 'A1'
11
- Rspreadsheet::Tools.convert_cell_coordinates_to_address([17,3]).should == 'C17'
12
- Rspreadsheet::Tools.convert_cell_coordinates_to_address([1,27]).should == 'AA1'
13
- Rspreadsheet::Tools.convert_cell_coordinates_to_address([1,39]).should == 'AM1'
14
- Rspreadsheet::Tools.convert_cell_coordinates_to_address([1,53]).should == 'BA1'
15
- Rspreadsheet::Tools.convert_cell_coordinates_to_address([1,702]).should == 'ZZ1'
16
- Rspreadsheet::Tools.convert_cell_coordinates_to_address([2,703]).should == 'AAA2'
17
- Rspreadsheet::Tools.convert_cell_coordinates_to_address([1048576,39]).should == 'AM1048576'
18
- Rspreadsheet::Tools.convert_cell_coordinates_to_address([2,781]).should == 'ADA2'
19
- Rspreadsheet::Tools.c2a([2,781]).should == 'ADA2'
20
- Rspreadsheet::Tools.c2a(2,781).should == 'ADA2'
21
- Rspreadsheet::Tools.a2c('ADA2').should == [2,781]
22
- Rspreadsheet::Tools.a2c('ADA','2').should == [2,781]
23
- (1..200).each { |i| Rspreadsheet::Tools.a2c(Rspreadsheet::Tools.c2a(1,i*10)).should == [1,i*10] }
3
+ describe Rspreadsheet::Tools, :focus do
4
+ before do
5
+ @tools = Rspreadsheet::Tools
6
+ end
7
+ it 'converts correctly cell adresses to coordinates' do
8
+ @tools.convert_cell_address_to_coordinates('A1').should == [1,1]
9
+ @tools.convert_cell_address_to_coordinates('C17').should == [17,3]
10
+ @tools.convert_cell_address_to_coordinates('AM1048576').should == [1048576,39]
11
+ @tools.convert_cell_address_to_coordinates('Am1048576').should == [1048576,39]
12
+ @tools.convert_cell_address_to_coordinates('aDa2').should == [2,781]
13
+ @tools.convert_cell_address_to_coordinates('Zz1').should == [1,702]
14
+ @tools.a2c('AdA2').should == [2,781]
15
+ @tools.a2c('ADA','2').should == [2,781]
16
+ end
17
+ it 'converts correctly cell coordinates to adresses' do
18
+ @tools.convert_cell_coordinates_to_address([1,1]).should == 'A1'
19
+ @tools.convert_cell_coordinates_to_address([17,3]).should == 'C17'
20
+ @tools.convert_cell_coordinates_to_address([1,27]).should == 'AA1'
21
+ @tools.convert_cell_coordinates_to_address([1,39]).should == 'AM1'
22
+ @tools.convert_cell_coordinates_to_address([1,53]).should == 'BA1'
23
+ @tools.convert_cell_coordinates_to_address([1,702]).should == 'ZZ1'
24
+ @tools.convert_cell_coordinates_to_address([2,703]).should == 'AAA2'
25
+ @tools.convert_cell_coordinates_to_address([1048576,39]).should == 'AM1048576'
26
+ @tools.convert_cell_coordinates_to_address([2,781]).should == 'ADA2'
27
+ @tools.c2a([2,781]).should == 'ADA2'
28
+ @tools.c2a(2,781).should == 'ADA2'
29
+ end
30
+ it 'conversions c2a and a2c are inverse of each other' do
31
+ (1..200).each { |i| @tools.a2c(@tools.c2a(1,i*10)).should == [1,i*10] }
32
+ end
33
+ it 'raises exception when given rubbisch' do
34
+ expect{ @tools.a2c('A1A') }.to raise_error
35
+ expect{ @tools.a2c('1A11') }.to raise_error
36
+ expect{ @tools.a2c('1A11') }.to raise_error
37
+ end
38
+ it 'converts correctly cell adresses given by components to coordinates' do
39
+ @tools.a2c('A','1').should eq [1,1]
40
+ @tools.a2c('C','17').should eq [17,3]
41
+ @tools.a2c('17','C',).should eq [17,3]
42
+ @tools.a2c('17','ZZ',).should eq [17,702]
43
+ end
44
+ it 'given two numbers converts them correctly even when "hidden" in strings' do
45
+ @tools.a2c('3','17').should eq [3,17]
46
+ @tools.a2c(21,'11.0').should eq [21,11]
47
+ @tools.a2c('23',22/2).should eq [23,11]
24
48
  end
25
49
  end
@@ -1,13 +1,13 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Rspreadsheet::Workbook, :focus=>true do
3
+ describe Rspreadsheet::Workbook do
4
4
  it 'has correct number of sheets' do
5
5
  book = Rspreadsheet::Workbook.new($test_filename)
6
6
  book.worksheets_count.should == 1
7
- book.worksheets[0].should be_nil
8
- book.worksheets[1].should be_kind_of(Rspreadsheet::Worksheet)
9
- book.worksheets[2].should be_nil
10
- book.worksheets[nil].should be_nil
7
+ book.worksheets(0).should be_nil
8
+ book.worksheets(1).should be_kind_of(Rspreadsheet::Worksheet)
9
+ book.worksheets(2).should be_nil
10
+ book.worksheets(nil).should be_nil
11
11
  end
12
12
  it 'freshly created has correctly namespaced xmlnode' do
13
13
  @xmlnode = Rspreadsheet::Workbook.new.xmlnode
@@ -28,10 +28,23 @@ describe Rspreadsheet::Workbook, :focus=>true do
28
28
  it 'nonemptycells behave correctly' do
29
29
  book = Rspreadsheet::Workbook.new()
30
30
  book.create_worksheet
31
- @sheet = book.worksheets[1]
31
+ @sheet = book.worksheets(1)
32
32
  @sheet.cells(3,3).value = 'data'
33
33
  @sheet.cells(5,7).value = 'data'
34
34
  @sheet.nonemptycells.collect{|c| c.coordinates}.should =~ [[3,3],[5,7]]
35
35
  end
36
-
36
+ it 'can create named sheets and they are the same as "numbered" ones' do
37
+ book = Rspreadsheet::Workbook.new
38
+ book.create_worksheet('test')
39
+ book.worksheets('test').should eq book.worksheets(1)
40
+ book.create_worksheet('another')
41
+ book.worksheets('another').should eq book.worksheets(2)
42
+ end
43
+ it 'can access sheets with brief syntax' do
44
+ book = Rspreadsheet::Workbook.new
45
+ book.create_worksheet('test')
46
+ book.worksheets('test').should be book.worksheets(1)
47
+ book['test'].should be book.worksheets(1)
48
+ book[1].should be book.worksheets(1)
49
+ end
37
50
  end
@@ -1,19 +1,21 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Rspreadsheet::Worksheet do
4
- before do
5
- @sheet = Rspreadsheet.new($test_filename).worksheets[1]
6
- end
7
- it 'contains nonempty xml in rows for testfile' do
8
- @sheet.rows(1).xmlnode.elements.size.should be >1
9
- end
10
- it 'uses detach_my_subnode_respect_repeated well' do
11
- @sheet.detach_my_subnode_respect_repeated(50, {:xml_items_node_name => 'table-row', :xml_repeated_attribute => 'number-rows-repeated'})
12
- @sheet.rows(50).detach_my_subnode_respect_repeated(12, {:xml_items_node_name => 'table-cell', :xml_repeated_attribute => 'number-columns-repeated'})
4
+ describe "from test workbook file" do
5
+ before do
6
+ @sheet = Rspreadsheet.new($test_filename).worksheets(1)
7
+ end
8
+ it 'contains nonempty xml in rows for testfile' do
9
+ @sheet.rows(1).xmlnode.elements.size.should be >1
10
+ end
11
+ it 'uses detach_my_subnode_respect_repeated well' do
12
+ @sheet.detach_my_subnode_respect_repeated(50, {:xml_items_node_name => 'table-row', :xml_repeated_attribute => 'number-rows-repeated'})
13
+ @sheet.rows(50).detach_my_subnode_respect_repeated(12, {:xml_items_node_name => 'table-cell', :xml_repeated_attribute => 'number-columns-repeated'})
14
+ end
13
15
  end
14
16
  end
15
17
 
16
- describe Rspreadsheet::Worksheet do
18
+ describe Rspreadsheet::Worksheet, :focus do
17
19
  before do
18
20
  book = Rspreadsheet.new
19
21
  @sheet = book.create_worksheet
@@ -26,6 +28,10 @@ describe Rspreadsheet::Worksheet do
26
28
  @xmlnode.namespaces.namespace.should_not be_nil
27
29
  @xmlnode.namespaces.namespace.prefix.should == 'table'
28
30
  end
31
+ it 'freshly created has correct name' do
32
+ @sheet2 = Rspreadsheet.new.create_worksheet('test')
33
+ @sheet2.name.should eq 'test'
34
+ end
29
35
  it 'remembers the value stored to A1 cell' do
30
36
  @sheet[1,1].should == nil
31
37
  @sheet[1,1] = 'test text'
@@ -42,7 +48,7 @@ describe Rspreadsheet::Worksheet do
42
48
  @sheet.cells(1,1).class.should == Rspreadsheet::Cell
43
49
  end
44
50
  it 'has name, which can be changed and is remembered' do
45
- @sheet.name.should be(nil)
51
+ @sheet.name.should_not be(nil) # it should have some default name
46
52
  @sheet.name = 'Icecream'
47
53
  @sheet.name.should == 'Icecream'
48
54
  @sheet.name = 'Cofee'
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.3
4
+ version: 0.2.4
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: 2014-11-19 00:00:00.000000000 Z
11
+ date: 2014-11-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: libxml-ruby
@@ -165,6 +165,8 @@ files:
165
165
  - ".kateproject"
166
166
  - ".kateproject.d/notes.txt"
167
167
  - ".travis.yml"
168
+ - ".yardopts"
169
+ - CHANGELOG.md
168
170
  - COPYING.txt
169
171
  - DEVEL_BLOG.md
170
172
  - GUIDE.md