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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b7c4756f4b74164e9aeafb35af79f923aff8a5fa
|
4
|
+
data.tar.gz: 04814801b5e23bd0d4077ffbe39a65fbc7401d67
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e9e8fd002a0fdd21bd453b62088df71ece09d107eb6b6b8217f6805e989e0c18d63d376d8e7c338b2b8c292f7d46deec8dab8b161372a59978b10b33990de417
|
7
|
+
data.tar.gz: 59db224cb0928ee5494f79583e6f340ecba729670688e39f0a64216647947a274022e73740f801bbe0e639984d7806d610484742805f469a2c8df0d033dfebaa
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
language: ruby
|
2
2
|
cache: bundler
|
3
3
|
rvm:
|
4
|
-
- 2.2.3
|
5
|
-
- 2.2.0
|
6
|
-
- 2.1.7
|
7
4
|
- 2.0.0
|
8
|
-
- 1.
|
5
|
+
- 2.1.10
|
6
|
+
- 2.2.6
|
7
|
+
- 2.3.3
|
8
|
+
- 2.4.0
|
9
9
|
script: 'bundle exec rake'
|
10
10
|
before_install:
|
11
|
+
- gem uninstall libxml-ruby
|
11
12
|
- gem update bundler
|
12
13
|
notifications:
|
13
14
|
email:
|
data/DEVEL_BLOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
See [GUIDE.md](GUIDE.md#conventions) for syntax conventions.
|
2
2
|
|
3
|
+
## Installation troubleshooting
|
4
|
+
|
5
|
+
After you have cloned the source run `bundle` command in gem directory to install needed tools. If there are any errors try to
|
6
|
+
|
7
|
+
1. Make sure that necessary tools are installed. In debian based distros you may try this `apt-get install make gcc`. In other distros, use their native way to install tools.
|
8
|
+
2. If you have installed `ruby-libxml` than you may want to comment out the line containing libxml-ruby dependency in rspreadseet.gemspec file before running bundle.
|
9
|
+
3. Comment out `guard` and `guard-rspec` lines from gemfile if you use ruby version less than 2.2.1.
|
10
|
+
|
3
11
|
## Ideas/wishlist
|
4
12
|
|
5
13
|
* In future inntroduce syntax like `sheet.range('C3:E4')` for mass operations.
|
@@ -13,6 +21,8 @@ See [GUIDE.md](GUIDE.md#conventions) for syntax conventions.
|
|
13
21
|
* implement to_csv
|
14
22
|
* longterm plan - go through other used libraries and try to find out whose syntax could be adopted, so this library is drop in replacement (possibly with some config options) for them
|
15
23
|
* iterative generation like this
|
24
|
+
* Currently the way you insert cells and rows is that you insert empty one and than copy values. It would be interesting to add "duplicate" method as well as other "copy pasting" stuff, which would be more forward compatible.
|
25
|
+
* Optionally allow negative coordinates count from end
|
16
26
|
|
17
27
|
```ruby
|
18
28
|
RSpreadsheet.generate('pricelist.ods') do
|
@@ -53,7 +63,7 @@ RSpreadsheet.generate('pricelist.ods') do
|
|
53
63
|
* array returned by rows of cells can have predefined dummy object as defaults for out of range indexes
|
54
64
|
|
55
65
|
|
56
|
-
##Guiding ideas
|
66
|
+
## Guiding ideas
|
57
67
|
* xml document is always synchronized with the data. So the save is trivial.
|
58
68
|
* 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.
|
59
69
|
* all cells and rows only server as proxy. they hold index and worksheet pointer and everytime read or write is done, the xml is newly searched. until there is a xmlnode caching we have no problem
|
@@ -62,6 +72,12 @@ RSpreadsheet.generate('pricelist.ods') do
|
|
62
72
|
## Known Issues
|
63
73
|
* currently there is a confusing syntax @worksheet.rows(1).cells[5] which returns a cell object in SIXT column, which is not intended. It is side effecto of Row#cells returning an array
|
64
74
|
|
75
|
+
## Release notes
|
76
|
+
|
77
|
+
2017-01
|
78
|
+
* basic image handling implemented (issue [#24](https://github.com/gorn/rspreadsheet/issues/24))
|
79
|
+
* bug corrected: inserted row was not empty, but rather copy of the row below.
|
80
|
+
|
65
81
|
## Developing this gem
|
66
82
|
|
67
83
|
### Automated testing
|
@@ -84,7 +100,7 @@ RSpreadsheet.generate('pricelist.ods') do
|
|
84
100
|
5. When happy, increment the version number and `git add .; git commit -am'commit message'; git push`
|
85
101
|
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.
|
86
102
|
|
87
|
-
gem
|
103
|
+
gem alternative to points 3-6
|
88
104
|
|
89
105
|
gem build rspreadsheet.gemspec -\ These two lines together are in install.sh
|
90
106
|
sudo gem install rspreadsheet-x.y.z.gem -/ which should be invoked from parent directory
|
data/GUIDE.md
CHANGED
@@ -28,6 +28,11 @@ You can mix these two at will, for example like this
|
|
28
28
|
@row[4] = 10
|
29
29
|
````
|
30
30
|
|
31
|
+
### Working with images
|
32
|
+
@sheet.insert_image_to('10.21mm','15mm','image.png')
|
33
|
+
i = @sheet.images.first
|
34
|
+
i.move_to('100mm','99.98mm')
|
35
|
+
|
31
36
|
## Examples
|
32
37
|
|
33
38
|
* [basic functionality](https://gist.github.com/gorn/42e33d086d9b4fda10ec)
|
data/Guardfile
CHANGED
@@ -13,13 +13,13 @@ end
|
|
13
13
|
# classical part
|
14
14
|
scope group: :normal
|
15
15
|
|
16
|
-
group :normal do
|
17
|
-
guard 'rspec' do watch_all end
|
16
|
+
group :normal, cmd: "bundle exec rspec" do
|
17
|
+
guard 'rspec', cmd: 'bundle exec rspec' do watch_all end
|
18
18
|
end
|
19
19
|
|
20
20
|
# see http://stackoverflow.com/questions/18501471/guard-how-to-run-specific-tags-from-w-in-guards-console
|
21
21
|
group :focus do
|
22
|
-
guard 'rspec',
|
22
|
+
guard 'rspec', cmd: 'bundle exec rspec --tag focus' do watch_all end
|
23
23
|
end
|
24
24
|
|
25
25
|
#group :f do
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
+
[![Build Status](https://api.travis-ci.org/gorn/rspreadsheet.svg?branch=master)](https://travis-ci.org/gorn/rspreadsheet) [![Coverage Status](https://coveralls.io/repos/gorn/rspreadsheet/badge.svg?branch=master&service=github)](https://coveralls.io/r/gorn/rspreadsheet) [![User opinion wanted](https://badge.waffle.io/gorn/rspreadsheet.png?label=user opinion wanted&title=User opinion wanted)](https://waffle.io/gorn/rspreadsheet)
|
1
2
|
|
2
|
-
[![Build Status](https://api.travis-ci.org/gorn/rspreadsheet.svg?branch=master)](https://travis-ci.org/gorn/rspreadsheet) [![Coverage Status](https://coveralls.io/repos/gorn/rspreadsheet/badge.png)](https://coveralls.io/r/gorn/rspreadsheet) [![User opinion wanted](https://badge.waffle.io/gorn/rspreadsheet.png?label=user opinion wanted&title=User opinion wanted)](https://waffle.io/gorn/rspreadsheet)
|
3
3
|
# rspreadsheet
|
4
4
|
|
5
5
|
Manipulating spreadsheets with Ruby. Read, **modify**, write or create new OpenDocument Spreadsheet files from ruby code.
|
@@ -32,7 +32,10 @@ sheet.row(5).cellvalues.sum
|
|
32
32
|
sheet.row(5).cells.sum{ |cell| cell.value.to_f }
|
33
33
|
|
34
34
|
# or set formula to a cell
|
35
|
-
sheet.A1.formula='=SUM(A2:A9)'
|
35
|
+
sheet.cell('A1').formula='=SUM(A2:A9)'
|
36
|
+
|
37
|
+
# insert company logo to the file
|
38
|
+
sheet.insert_image_to('10mm','15mm','company_logo.png')
|
36
39
|
|
37
40
|
# iterating over list of people and displaying the data
|
38
41
|
total = 0
|
@@ -47,11 +50,8 @@ book.save
|
|
47
50
|
book.save('different_filename.ods')
|
48
51
|
```
|
49
52
|
|
50
|
-
|
51
|
-
|
52
|
-
* [basic functionality](https://gist.github.com/gorn/42e33d086d9b4fda10ec)
|
53
|
-
* [extended examples](https://gist.github.com/gorn/b432e6a69e82628349e6) of lots of alternative syntax
|
54
|
-
* [GUIDE.md](GUIDE.md) some other notes
|
53
|
+
* [More examples](https://gist.github.com/gorn/b432e6a69e82628349e6) of lots of alternative syntax **you can leave you comments and suggestions there**
|
54
|
+
* [Guide to using Rspreadsheet](GUIDE.md) some other notes
|
55
55
|
|
56
56
|
## Installation and Configuration
|
57
57
|
|
@@ -76,12 +76,12 @@ Alhought this gem is still in beta stage I use in everyday and it works fine. Cu
|
|
76
76
|
|
77
77
|
### Programmers
|
78
78
|
|
79
|
-
1. [Fork it](http://github.com/gorn/rspreadsheet/fork)
|
79
|
+
1. [Fork it](http://github.com/gorn/rspreadsheet/fork) and clone it ([troubleshooting](https://github.com/gorn/rspreadsheet/blob/master/DEVEL_BLOG.md#installation-troubleshooting))
|
80
80
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
81
81
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
82
82
|
4. Push to the branch (`git push origin my-new-feature`)
|
83
83
|
5. Create new Pull Request
|
84
|
-
|
84
|
+
|
85
85
|
## Why another OpenDocument spreadsheet gem?
|
86
86
|
|
87
87
|
I would be glad to safe myself work, but surprisingly, there are not that many gems for OpenDocument spreadsheets. Most of them also look abandoned and inactive, or can only read or write spreadsheets, but not modify them. I have investigated these options (you might as well):
|
@@ -1,49 +1,112 @@
|
|
1
|
-
|
1
|
+
if RUBY_VERSION > '2.1'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def first_diff(node2)
|
15
|
-
where = self.path.split('/').last
|
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
|
16
14
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
refine LibXML::XML::Node do
|
16
|
+
def equals?(node2); raise 'nic' end
|
17
|
+
def ==(node2)
|
18
|
+
self.simplification_of?(node2) and node2.simplification_of?(self)
|
19
|
+
end
|
20
|
+
# if node2 contains at least all that I do
|
21
|
+
def simplification_of?(node2)
|
22
|
+
first_diff(node2).nil?
|
23
|
+
end
|
24
|
+
# return first difference where self has something more than node2 does
|
25
|
+
def first_diff(node2)
|
26
|
+
where = self.path.split('/').last
|
27
|
+
|
28
|
+
return "#{where}> Equivalent node does not exist: #{self.name} != NOTHING" if node2.nil?
|
29
|
+
return "#{where}> Names are different: #{self.name} != #{node2.name}" if (self.name != node2.name)
|
30
|
+
self.attributes.each do |attr|
|
31
|
+
return "#{where}> Attribute #{attr} have diffent values: #{attr.value} != #{node2.attributes[attr.name]}" unless node2.attributes[attr.name] == attr.value
|
32
|
+
end
|
33
|
+
|
34
|
+
elems1 = self.elements
|
35
|
+
elems2 = node2.elements
|
36
|
+
return "#{where}> elements have different number of subelements #{elems1.length} != #{elems2.length}" if (elems1.length != elems2.length)
|
37
|
+
|
38
|
+
elems1.each_index do |i|
|
39
|
+
raise "Nil for i=#{i}" if elems1[i].nil?
|
40
|
+
if (elems1[i].node_type_name == 'text')
|
41
|
+
if elems2[i].nil? || (elems1[i].to_s) != (elems2[i].to_s)
|
42
|
+
return "#{where}> #{i+1}th text subelements are different: #{elems1[i].to_s} != #{elems2[i].to_s}"
|
43
|
+
end
|
44
|
+
elsif (elems1[i].node_type_name == 'element')
|
45
|
+
unless elems1[i].simplification_of?(elems2[i])
|
46
|
+
return "#{where}/[#{i+1}]#{elems1[i].first_diff(elems2[i])}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
return nil
|
52
|
+
end
|
53
|
+
def elements
|
54
|
+
result = []
|
55
|
+
each_element { |e| result << e }
|
56
|
+
return result
|
57
|
+
end
|
21
58
|
end
|
22
59
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
60
|
+
end # module ClassExtensions
|
61
|
+
|
62
|
+
else # Monkeypatching
|
63
|
+
|
64
|
+
class Array
|
65
|
+
def sum(identity = 0, &block)
|
66
|
+
if block_given?
|
67
|
+
map(&block).sum(identity)
|
68
|
+
else
|
69
|
+
inject(0){ |sum, element| sum.to_f + element.to_f } || identity
|
31
70
|
end
|
32
71
|
end
|
33
|
-
|
34
|
-
return nil
|
35
|
-
end
|
36
|
-
def equals?(node2) #TODO redefine == with this
|
37
|
-
self.simplification_of?(node2) and node2.simplification_of?(self)
|
38
72
|
end
|
39
|
-
end
|
40
73
|
|
41
|
-
class
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
74
|
+
class LibXML::XML::Node
|
75
|
+
def ==(node2)
|
76
|
+
self.simplification_of?(node2) and node2.simplification_of?(self)
|
77
|
+
end
|
78
|
+
# if node2 contains at least all that I do
|
79
|
+
def simplification_of?(node2)
|
80
|
+
first_diff(node2).nil?
|
81
|
+
end
|
82
|
+
# return first difference where self has something more than node2 does
|
83
|
+
def first_diff(node2)
|
84
|
+
where = self.path.split('/').last
|
85
|
+
|
86
|
+
return "#{where}> Equivalent node does not exist: #{self.name} != NOTHING" if node2.nil?
|
87
|
+
return "#{where}> Names are different: #{self.name} != #{node2.name}" if (self.name != node2.name)
|
88
|
+
self.attributes.each do |attr|
|
89
|
+
return "#{where}> Attribute #{attr} have diffent values: #{attr.value} != #{node2.attributes[attr.name]}" unless node2.attributes[attr.name] == attr.value
|
90
|
+
end
|
91
|
+
|
92
|
+
elems1 = self.elements
|
93
|
+
elems2 = node2.elements
|
94
|
+
# return "#{where}> elements have different number of subelements #{elems1.length} != #{elems2.length}" if (elems1.length != elems2.length)
|
95
|
+
elems1.length.times do |i|
|
96
|
+
if (elems1[i].node_type_name == 'text') && ((elems1[i].to_s != elems2[i].to_s) )
|
97
|
+
return "#{where}> #{i+1}th text subelements are different: #{elems1[i].to_s} != #{elems2[i].to_s}"
|
98
|
+
elsif (elems1[i].node_type_name == 'element') && (!elems1[i].simplification_of?(elems2[i]))
|
99
|
+
return "#{where}/[#{i+1}]#{elems1[i].first_diff(elems2[i])}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
return nil
|
104
|
+
end
|
105
|
+
def elements
|
106
|
+
result = []
|
107
|
+
each_element { |e| result << e }
|
108
|
+
return result
|
47
109
|
end
|
48
110
|
end
|
111
|
+
|
49
112
|
end
|
data/lib/rspreadsheet.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'rspreadsheet/version'
|
2
2
|
require 'rspreadsheet/workbook'
|
3
3
|
require 'rspreadsheet/worksheet'
|
4
|
-
require 'helpers/class_extensions'
|
5
4
|
require 'helpers/configuration'
|
5
|
+
# refinements
|
6
|
+
require 'helpers/class_extensions'
|
6
7
|
|
7
8
|
module Rspreadsheet
|
8
9
|
extend Configuration
|
data/lib/rspreadsheet/cell.rb
CHANGED
@@ -3,12 +3,15 @@
|
|
3
3
|
# @title rspreadsheet Cell
|
4
4
|
|
5
5
|
require 'andand'
|
6
|
-
require 'rspreadsheet/
|
6
|
+
require 'rspreadsheet/xml_tied_item'
|
7
7
|
require 'date'
|
8
|
+
require 'time'
|
8
9
|
require 'bigdecimal'
|
9
10
|
require 'bigdecimal/util' # for to_d method
|
11
|
+
require 'helpers/class_extensions'
|
10
12
|
|
11
13
|
module Rspreadsheet
|
14
|
+
using ClassExtensions if RUBY_VERSION > '2.1'
|
12
15
|
|
13
16
|
###
|
14
17
|
# Represents a cell in spreadsheet which has coordinates, contains value, formula and can be formated.
|
@@ -17,30 +20,33 @@ module Rspreadsheet
|
|
17
20
|
# @worksheet.cells(5,2)
|
18
21
|
#
|
19
22
|
# 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.
|
23
|
+
# More precisely it is equvalient to @worksheet.cells(5,2).value.
|
24
|
+
#
|
20
25
|
|
21
26
|
class Cell < XMLTiedItem
|
22
|
-
attr_accessor :worksheet
|
23
|
-
|
24
|
-
|
25
|
-
|
27
|
+
attr_accessor :worksheet
|
28
|
+
attr_reader :rowi
|
29
|
+
InternalDateFormat = '%Y-%m-%d'
|
30
|
+
InternalTimeFormat = 'PT%HH%MM%SS'
|
31
|
+
# InternalTimeFormat = 'PT%HH%MM%SS,%LS'
|
26
32
|
|
27
|
-
|
28
|
-
|
33
|
+
# @!group XMLTiedItem related methods and extensions
|
34
|
+
def xml_options; {:xml_items_node_name => 'table-cell', :xml_repeated_attribute => 'number-columns-repeated'} end
|
29
35
|
def parent; row end
|
30
|
-
def
|
31
|
-
|
36
|
+
def coli; index end
|
37
|
+
|
32
38
|
def set_rowi(arowi); @rowi = arowi end # this should ONLY be used by parent row
|
33
39
|
def initialize(aworksheet,arowi,acoli)
|
34
40
|
raise "First parameter should be Worksheet object not #{aworksheet.class}" unless aworksheet.kind_of?(Rspreadsheet::Worksheet)
|
35
41
|
@worksheet = aworksheet
|
36
42
|
@rowi = arowi
|
37
|
-
|
43
|
+
initialize_xml_tied_item(row,acoli)
|
38
44
|
end
|
39
45
|
def row; @worksheet.rows(rowi) end
|
40
46
|
def coordinates; [rowi,coli] end
|
41
47
|
def to_s; value.to_s end
|
42
48
|
def valuexml; self.valuexmlnode.andand.inner_xml end
|
43
|
-
def valuexmlnode; self.xmlnode.
|
49
|
+
def valuexmlnode; self.xmlnode.elements.first end
|
44
50
|
# use this to find node in cell xml. ex. xmlfind('.//text:a') finds all link nodes
|
45
51
|
def valuexmlfindall(path)
|
46
52
|
valuexmlnode.nil? ? [] : valuexmlnode.find(path)
|
@@ -49,7 +55,7 @@ class Cell < XMLTiedItem
|
|
49
55
|
valuexmlfindall(path).first
|
50
56
|
end
|
51
57
|
def inspect
|
52
|
-
"#<Rspreadsheet::Cell
|
58
|
+
"#<Rspreadsheet::Cell\n row:#{rowi}, col:#{coli} address:#{address}\n type: #{guess_cell_type.to_s}, value:#{value}\n mode: #{mode}, format: #{format.inspect}\n>"
|
53
59
|
end
|
54
60
|
def value
|
55
61
|
gt = guess_cell_type
|
@@ -58,7 +64,8 @@ class Cell < XMLTiedItem
|
|
58
64
|
when gt == nil then nil
|
59
65
|
when gt == Float then xmlnode.attributes['value'].to_f
|
60
66
|
when gt == String then xmlnode.elements.first.andand.content.to_s
|
61
|
-
when gt == Date then Date.strptime(xmlnode.attributes['date-value'].to_s,
|
67
|
+
when gt == Date then Date.strptime(xmlnode.attributes['date-value'].to_s, InternalDateFormat)
|
68
|
+
when gt == Time then Time.strptime(xmlnode.attributes['time-value'].to_s, InternalTimeFormat)
|
62
69
|
when gt == :percentage then xmlnode.attributes['value'].to_f
|
63
70
|
when gt == :currency then xmlnode.attributes['value'].to_d
|
64
71
|
end
|
@@ -86,8 +93,14 @@ class Cell < XMLTiedItem
|
|
86
93
|
when gt == Date then
|
87
94
|
remove_all_value_attributes_and_content(xmlnode)
|
88
95
|
set_type_attribute('date')
|
89
|
-
|
90
|
-
|
96
|
+
avalue = avalue.strftime(InternalDateFormat)
|
97
|
+
Tools.set_ns_attribute(xmlnode,'office','date-value', avalue)
|
98
|
+
xmlnode << Tools.prepare_ns_node('text','p', avalue)
|
99
|
+
when gt == Time then
|
100
|
+
remove_all_value_attributes_and_content(xmlnode)
|
101
|
+
set_type_attribute('time')
|
102
|
+
Tools.set_ns_attribute(xmlnode,'office','time-value', avalue.strftime(InternalTimeFormat))
|
103
|
+
xmlnode << Tools.prepare_ns_node('text','p', avalue.strftime('%H:%M'))
|
91
104
|
when gt == :percentage then
|
92
105
|
remove_all_value_attributes_and_content(xmlnode)
|
93
106
|
set_type_attribute('percentage')
|
@@ -105,6 +118,7 @@ class Cell < XMLTiedItem
|
|
105
118
|
def remove_all_value_attributes_and_content(node=xmlnode)
|
106
119
|
if att = Tools.get_ns_attribute(node, 'office','value') then att.remove! end
|
107
120
|
if att = Tools.get_ns_attribute(node, 'office','date-value') then att.remove! end
|
121
|
+
if att = Tools.get_ns_attribute(node, 'office','time-value') then att.remove! end
|
108
122
|
if att = Tools.get_ns_attribute(node, 'table','formula') then att.remove! end
|
109
123
|
node.content=''
|
110
124
|
end
|
@@ -120,6 +134,7 @@ class Cell < XMLTiedItem
|
|
120
134
|
when gct == Float then :float
|
121
135
|
when gct == String then :string
|
122
136
|
when gct == Date then :date
|
137
|
+
when gct == Time then :time
|
123
138
|
when gct == :percentage then :percentage
|
124
139
|
when gct == :unassigned then :unassigned
|
125
140
|
when gct == :currency then :currency
|
@@ -132,28 +147,29 @@ class Cell < XMLTiedItem
|
|
132
147
|
# try guessing by value
|
133
148
|
valueguess = case avalue
|
134
149
|
when Numeric then Float
|
150
|
+
when Time then Time
|
135
151
|
when Date then Date
|
136
152
|
when String,nil then nil
|
137
153
|
else nil
|
138
154
|
end
|
139
155
|
result = valueguess
|
140
156
|
|
141
|
-
if valueguess.nil? # valueguess is most important
|
142
|
-
# if not succesfull then try guessing by type from node xml
|
157
|
+
if valueguess.nil? # valueguess is most important if not succesfull then try guessing by type from node xml
|
143
158
|
typ = xmlnode.nil? ? 'N/A' : xmlnode.attributes['value-type']
|
144
159
|
typeguess = case typ
|
145
160
|
when nil then nil
|
146
161
|
when 'float' then Float
|
147
162
|
when 'string' then String
|
163
|
+
when 'time' then Time
|
148
164
|
when 'date' then Date
|
149
165
|
when 'percentage' then :percentage
|
150
166
|
when 'N/A' then :unassigned
|
151
167
|
when 'currency' then :currency
|
152
168
|
else
|
153
|
-
if xmlnode.
|
169
|
+
if xmlnode.elements.size == 0
|
154
170
|
nil
|
155
171
|
else
|
156
|
-
raise "Unknown type at #{coordinates.to_s} from #{xmlnode.to_s} /
|
172
|
+
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
173
|
end
|
158
174
|
end
|
159
175
|
|
@@ -209,6 +225,11 @@ class Cell < XMLTiedItem
|
|
209
225
|
Tools.set_ns_attribute(xmlnode,'table','formula','of:'+formulastring.to_s)
|
210
226
|
end
|
211
227
|
def blank?; self.type==:empty or self.type==:unassigned end
|
228
|
+
|
229
|
+
def border_top; format.border_top end
|
230
|
+
def border_right; format.border_right end
|
231
|
+
def border_bottom; format.border_bottom end
|
232
|
+
def border_left; format.border_left end
|
212
233
|
|
213
234
|
end
|
214
235
|
|
@@ -249,6 +270,7 @@ class CellFormat
|
|
249
270
|
end
|
250
271
|
def background_color=(value); set_cell_style_node_attribute('background-color', value) end
|
251
272
|
def set_cell_style_node_attribute(attribute_name,value)
|
273
|
+
@cell.detach if @cell.mode != :regular
|
252
274
|
if cell_style_node.nil?
|
253
275
|
self.create_cell_style_node
|
254
276
|
raise 'Style node was not correctly initialized' if cell_style_node.nil?
|
@@ -300,10 +322,79 @@ class CellFormat
|
|
300
322
|
def currency
|
301
323
|
Tools.get_ns_attribute_value(cellnode,'office','currency',nil)
|
302
324
|
end
|
325
|
+
#returns object representing top border of the cell
|
326
|
+
def top; @top ||= Border.new(self,:top) end
|
327
|
+
def bottom; @bottom ||= Border.new(self,:bottom) end
|
328
|
+
def left; @left ||= Border.new(self,:left) end
|
329
|
+
def right; @right ||= Border.new(self,:right) end
|
330
|
+
alias :border_top :top
|
331
|
+
alias :border_right :right
|
332
|
+
alias :border_bottom :bottom
|
333
|
+
alias :border_left :left
|
334
|
+
|
335
|
+
def inspect
|
336
|
+
"#<Rspreadsheet::CellFormat bold:#{bold?.inspect}, borders:#{top.get_value_string.inspect} #{right.get_value_string.inspect} #{bottom.get_value_string.inspect} #{left.get_value_string.inspect}>"
|
337
|
+
end
|
338
|
+
|
303
339
|
end
|
304
340
|
|
341
|
+
# represents one of the borders of a cell
|
342
|
+
class Border
|
343
|
+
def initialize(cellformat,side)
|
344
|
+
@cellformat = cellformat
|
345
|
+
@side = side.to_s
|
346
|
+
raise "Wrong side of border object, can be top, bottom, left or right" unless ['left','right','top','bottom'].include? @side
|
347
|
+
end
|
348
|
+
def cellnode; @cell.xmlnode end
|
349
|
+
def attribute_name; "border-#{@side}" end
|
350
|
+
|
351
|
+
def width=(value); set_border_string_part(1, value) end
|
352
|
+
def style=(value); set_border_string_part(2, value.to_s) end
|
353
|
+
def color=(value); set_border_string_part(3, value) end
|
354
|
+
def width; get_border_string_part(1).to_f end
|
355
|
+
def style; get_border_string_part(2) end
|
356
|
+
def color; get_border_string_part(3) end
|
357
|
+
def delete
|
358
|
+
@cellformat.set_cell_style_node_attribute(attribute_name, 'none')
|
359
|
+
end
|
360
|
+
|
361
|
+
|
362
|
+
## internals
|
363
|
+
|
364
|
+
# set parth-th part of string which represents the border. String looks like "0.06pt solid #00ee00"
|
365
|
+
# part is 1 for width, 2 for style or 3 for color
|
366
|
+
def set_border_string_part(part,value)
|
367
|
+
current_value = @cellformat.get_cell_style_node_attribute(attribute_name)
|
368
|
+
|
369
|
+
if current_value.nil? or (current_value=='none')
|
370
|
+
value_array = ['0.75pt', 'solid', '#000000'] # set default values
|
371
|
+
else
|
372
|
+
value_array = current_value.split(' ')
|
373
|
+
end
|
374
|
+
raise 'Strange border attribute value. Does not have 3 parts' unless value_array.length == 3
|
375
|
+
value_array[part-1]=value
|
376
|
+
@cellformat.set_cell_style_node_attribute(attribute_name, value_array.join(' '))
|
377
|
+
end
|
378
|
+
|
379
|
+
def get_border_string_part(part)
|
380
|
+
current_value = @cellformat.get_cell_style_node_attribute(attribute_name) || @cellformat.get_cell_style_node_attribute('border')
|
381
|
+
if current_value.nil? or (current_value=='none')
|
382
|
+
return nil
|
383
|
+
else
|
384
|
+
value_array = current_value.split(' ')
|
385
|
+
raise 'Strange border attribute value. Does not have 3 parts' unless value_array.length == 3
|
386
|
+
return value_array[part-1]
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
def get_value_string
|
391
|
+
@cellformat.get_cell_style_node_attribute(attribute_name)
|
392
|
+
end
|
393
|
+
|
305
394
|
end
|
306
395
|
|
396
|
+
end # module
|
397
|
+
|
307
398
|
|
308
399
|
|
309
400
|
|