rspreadsheet 0.0.5 → 0.0.6

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: 152586e2f35f91ad2793d61bdd9b1d2f3d497f2a
4
- data.tar.gz: 891cd0526d5c16c065bbced2da5dab48586d50a2
3
+ metadata.gz: c147702da70bb4a26183164ed522e854d65ffb60
4
+ data.tar.gz: 7bac02b0ac665aebb70232c06d02e07a487a3f61
5
5
  SHA512:
6
- metadata.gz: 82757075eefa5c3d7fbe287237aff3deca67242d893b6cc64211f51c10d4053d8f739515ec9df89ee7d1eb9761fe96126c9ce270fe7d74276563e1ce1cf7475f
7
- data.tar.gz: ce78296bd79b804f132fd7d9bf2358cb27fba64a0711e0b126512dcf3b7ea5730cc2fd7f21bef1f9cc3459451a92c81985daac7c6833f4194d7a167557900ffe
6
+ metadata.gz: 27c8e0ca9ea36ee65a8316a42ba2f993b10c4282ffe1447b3be4e2db4123a682e00c4a161b32f97cd2aa39d4e04402b521404950f8b29afdb9d456d22d63892e
7
+ data.tar.gz: fc92b43e46ba1157b294810dfecd524eba13e95c773357a9d65590a866b315200fc9fd616c5dcbc7bdfb9f5d73d50a667eababff5af7135f3e3f74ae83519e30
data/.gitignore CHANGED
@@ -22,6 +22,7 @@ build/
22
22
  /doc/
23
23
  /rdoc/
24
24
  .~lock*
25
+ .*kate-swp
25
26
 
26
27
  ## Environment normalisation:
27
28
  /.bundle/
@@ -31,8 +32,8 @@ build/
31
32
  # intended to run in multiple environments; otherwise, check them in:
32
33
 
33
34
  Gemfile.lock
34
- # .ruby-version
35
- # .ruby-gemset
35
+ .ruby-version
36
+ .ruby-gemset
36
37
 
37
38
  # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
38
39
  .rvmrc
data/DEVEL_BLOG.md CHANGED
@@ -2,6 +2,7 @@ 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.
5
6
  * In future inntroduce syntax like ``sheet.range('C3:E4')`` for mass operations. Also maybe ``sheet.cells('C3')`` or ``sheet.cells(3, 'C')`` etc.
6
7
  * Trying to make row Enumerable - perhaps skipping empty or undefined cells.
7
8
  * Accessors for nonempty/defined cells.
@@ -9,12 +10,17 @@ See [GUIDE.md](GUIDE.md#conventions) for syntax conventions.
9
10
  * Allow any of these:
10
11
  * ``book['Spring 2014']`` as alternative to ``book.worksheets('Spring 2014')`` ?
11
12
  * ``sheet.cells.F13`` as alternative to ``sheet.cells[14,5]`` ?
12
-
13
+
14
+ Guiding ideas
15
+ * xml document is always synchronized with the data. So the save is trivial.
16
+ * 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.
17
+
18
+
13
19
  ## Developing this gem
14
20
 
15
21
  ### Automated testing
16
22
 
17
- * ``guard`` will get tested any changes as soon as they are summitted
23
+ * ``budele exec guard`` will get tested any changes as soon as they are summitted
18
24
  * ``rake spec`` runs all test manually
19
25
 
20
26
  ### Automated utilities
@@ -37,3 +43,7 @@ alternative way using ``gem`` command
37
43
  gem push rspreadsheet-x.y.z.gem
38
44
 
39
45
 
46
+ ### Naming conventions
47
+
48
+ * create_xxx - creates object and inserts it where necessary
49
+ * prepare_xxx - create object, but does not insert it anywhere
data/GUIDE.md CHANGED
@@ -13,13 +13,13 @@ p sheet[1,1].class # => String
13
13
  p sheet[1,1] # => "My top 5"
14
14
 
15
15
  # These are all the same values - alternative syntax
16
+ p sheet.rows(1).cells(0).value
17
+ p sheet.cells(1,1).value
16
18
  p sheet.A1
17
19
  p sheet[1,1]
18
- p sheet.cells(0,0).value
19
- p sheet.rows(0).cells(0).value
20
20
 
21
21
  # How to inspect/manipulate the Cell object
22
- sheet.cells(1,1) # => Rspreadsheet::Cell
22
+ sheet.cells(1,1) # => Rspreadsheet::Cell
23
23
  sheet.cells(1,1).format
24
24
  sheet.cells(1,1).format.size = 15
25
25
  sheet.cells(1,1).format.weight = bold
data/Guardfile CHANGED
@@ -1,7 +1,7 @@
1
1
  guard 'rspec' do
2
2
  # watch /lib/ files
3
3
  watch(%r{^lib/(.+).rb$}) do |m|
4
- "spec/rspreadsheet_spec.rb"
4
+ "spec/#{m[1]}_spec.rb"
5
5
  end
6
6
 
7
7
  # watch /spec/ files
data/investigate.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'xml'
2
+
3
+ ns = XML::Namespace.new(XML::Node.new('xxx'), 'soap', 'http://schemas.xmlsoap.org/soap/envelope/')
4
+
5
+
6
+ d = XML::Document.new
7
+ nr = XML::Node.new('root', 'text')
8
+ d.root = nr
9
+ n2 = XML::Node.new('element', 'content')
10
+ nr << n2
11
+ puts d.to_s
12
+
13
+ n2.namespaces.namespace = ns
14
+ nr.namespaces.namespace = ns
15
+ puts d.to_s
16
+
17
+ n2.namespaces.namespace = ns
18
+ puts d.to_s
19
+
@@ -1,36 +1,48 @@
1
1
  class LibXML::XML::Node
2
+ def new_with_ns(namespace,name)
3
+ ns = self.namespaces.find_by_prefix('table') || LibXML::XML::Namespace.new(self, 'table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0')
4
+ self.namespaces.namespace = ns
5
+ self
6
+ LibXML::XML::Node.new('table-row')
7
+ end
2
8
  def elements
3
9
  result = []
4
10
  each_element { |e| result << e }
5
11
  return result
6
12
  end
7
13
  # if node2 contains at least all that I do
8
- def simpifation_of?(node2)
9
- return false if (self.name != node2.name)
14
+ def simplification_of?(node2)
15
+ first_diff(node2).nil?
16
+ end
17
+ # return first difference where self has something more than node2 does
18
+ def first_diff(node2)
19
+ where = self.path.split('/').last
20
+
21
+ return "#{where}> Equivalent node does not exist: #{self.name} != NOTHING" if node2.nil?
22
+ return "#{where}> Names are different: #{self.name} != #{node2.name}" if (self.name != node2.name)
10
23
  self.attributes.each do |attr|
11
- return false unless node2.attributes[attr.name] == attr.value
24
+ return "#{where}> Attribute #{attr} have diffent values: #{attr.value} != #{node2.attributes[attr.name]}" unless node2.attributes[attr.name] == attr.value
12
25
  end
13
26
 
14
27
  elems1 = self.elements
15
28
  elems2 = node2.elements
16
- return false if (elems1.length != elems2.length)
29
+ # return "#{where}> elements have different number of subelements #{elems1.length} != #{elems2.length}" if (elems1.length != elems2.length)
17
30
  elems1.length.times do |i|
18
- unless
19
- case elems1[i].node_type_name
20
- when 'text'
21
- (elems1[i].to_s == elems2[i].to_s)
22
- when 'element'
23
- elems1[i].simpifation_of?(elems2[i])
24
- else true
25
- end
26
- then
27
- return false
31
+ if (elems1[i].node_type_name == 'text') && ((elems1[i].to_s != elems2[i].to_s) )
32
+ return "#{where}> #{i+1}th text subelements are different: #{elems1[i].to_s} != #{elems2[i].to_s}"
33
+ elsif (elems1[i].node_type_name == 'element') && (!elems1[i].simplification_of?(elems2[i]))
34
+ return "#{where}/[#{i+1}]#{elems1[i].first_diff(elems2[i])}"
28
35
  end
29
36
  end
30
37
 
31
- return true
38
+ return nil
39
+ end
40
+ def equals?(node2) #TODO redefine == with this
41
+ self.simplification_of?(node2) and node2.simplification_of?(self)
32
42
  end
33
- def equals?(node2)
34
- simpifation_of?(node2) and node2.simpifation_of?(self)
43
+ def add_table_namesepace
44
+ ns = self.namespaces.find_by_prefix('table') || LibXML::XML::Namespace.new(self, 'table', 'urn:oasis:names:tc:opendocument:xmlns:table:1.0')
45
+ self.namespaces.namespace = ns
46
+ self
35
47
  end
36
48
  end
@@ -1,54 +1,166 @@
1
1
  require 'andand'
2
2
 
3
3
  module Rspreadsheet
4
+
4
5
  class Cell
5
- attr_reader :value,:col,:row, :source_node
6
- def initialize(arow,acol,source_node=nil)
7
- @col = acol
8
- @row = arow
9
- @source_node = source_node
10
- unless @source_node.nil?
11
- @type = @source_node.attributes['value-type'].to_s
12
- if (@source_node.children.size == 0) and (not @source_node.attributes?)
13
- @value = nil
14
- else
15
- # here you also ned to read style not only value
16
- @value = case @type
17
- when 'float'
18
- @source_node.attributes['value'].to_f
19
- when 'string'
20
- @source_node.elements.first.andand.content.to_s
21
- when 'date'
22
- Date.strptime(@source_node.attributes['date-value'].to_s, '%Y-%m-%d')
23
- when 'percentage'
24
- @source_node.attributes['value'].to_f
25
- else
26
- if @source_node.children.size == 0
27
- nil
28
- else
29
- nil
30
- # raise "Unknown type from #{@source_node.to_s} / children size=#{@source_node.children.size.to_s} / type=#{@type}"
31
- end
32
- end
33
- end
6
+ attr_reader :col, :xmlnode
7
+ attr_reader :parent_row # for debug only
8
+ attr_accessor :mode
9
+ def self.empty_cell_node
10
+ LibXML::XML::Node.new('table-cell',nil, Tools.get_namespace('table'))
11
+ end
12
+ def initialize(aparent_row,coli,asource_node=nil)
13
+ raise "First parameter should be Row object not #{aparent_row.class}" unless aparent_row.kind_of?(Rspreadsheet::Row)
14
+ @mode = :regular
15
+ @parent_row = aparent_row
16
+ if asource_node.nil?
17
+ asource_node = Cell.empty_cell_node
34
18
  end
19
+ @xmlnode = asource_node
20
+ @col = coli
35
21
  end
36
- def to_s
37
- value
22
+ def ns_table; @parent_row.xmlnode.doc.root.namespaces.find_by_prefix('table') end
23
+ def ns_office; @parent_row.xmlnode.doc.root.namespaces.find_by_prefix('office') end
24
+ def ns_text; @parent_row.xmlnode.doc.root.namespaces.find_by_prefix('text') end
25
+ def to_s; value end
26
+ def xml; self.source_node.to_s; end
27
+ def value_xml; self.source_node.children.first.children.first.to_s; end
28
+ def coordinates; [row,col]; end
29
+ def row; @parent_row.row; end
30
+ def value
31
+ gt = guess_cell_type
32
+ if (@mode == :regular) or (@mode == @repeated)
33
+ case
34
+ when gt == nil then nil
35
+ when gt == Float then @xmlnode.attributes['value'].to_f
36
+ when gt == String then @xmlnode.elements.first.andand.content.to_s
37
+ when gt == Date then Date.strptime(@xmlnode.attributes['date-value'].to_s, '%Y-%m-%d')
38
+ when gt == 'percentage' then @xmlnode.attributes['value'].to_f
39
+ end
40
+ elsif @mode == :outbound # needs to check whether it is still unbound
41
+ if parent_row.still_out_of_used_range?
42
+ nil
43
+ else
44
+ parent_row.normalize.cells(@col).value
45
+ end
46
+ else
47
+ raise "Unknown cell mode #{@mode}"
48
+ end
38
49
  end
39
50
  def value=(avalue)
40
- @value=avalue
41
- self
51
+ if @mode == :regular
52
+ gt = guess_cell_type(avalue)
53
+ case
54
+ when gt == nil then raise 'This value type is not storable to cell'
55
+ when gt == Float
56
+ set_type_attribute('float')
57
+ remove_all_value_attributes_and_content(@xmlnode)
58
+ Tools.set_ns_attribute(@xmlnode,'office','value', avalue.to_s)
59
+ @xmlnode << LibXML::XML::Node.new('p', avalue.to_f.to_s, ns_text)
60
+ when gt == String then
61
+ set_type_attribute('string')
62
+ remove_all_value_attributes_and_content(@xmlnode)
63
+ @xmlnode << LibXML::XML::Node.new('p', avalue.to_s, ns_text)
64
+ when gt == Date then
65
+ set_type_attribute('date')
66
+ remove_all_value_attributes_and_content(@xmlnode)
67
+ Tools.set_ns_attribute(@xmlnode,'office','date-value', avalue.strftime('%Y-%m-%d'))
68
+ @xmlnode << LibXML::XML::Node.new('p', avalue.strftime('%Y-%m-%d'), ns_text)
69
+ when gt == 'percentage'
70
+ set_type_attribute('float')
71
+ remove_all_value_attributes_and_content(@xmlnode)
72
+ Tools.set_ns_attribute(@xmlnode,'office','value', avalue.to_f.to_s)
73
+ @xmlnode << LibXML::XML::Node.new('p', (avalue.to_f*100).round.to_s+'%', ns_text)
74
+ end
75
+ elsif (@mode == :repeated) or (@mode == :outbound ) # Cell did not exist individually yet, detach row and create editable cell
76
+ row = @parent_row.detach
77
+ row.cells(@col).value = avalue
78
+ else
79
+ raise "Unknown cell mode #{@mode}"
80
+ end
42
81
  end
43
- def xml
44
- self.source_node.to_s
82
+ def remove_all_value_attributes_and_content(node)
83
+ if att = Tools.get_ns_attribute(@xmlnode, 'office','value') then att.remove! end
84
+ if att = Tools.get_ns_attribute(@xmlnode, 'office','date-value') then att.remove! end
85
+ @xmlnode.content=''
45
86
  end
46
- def value_xml
47
- # self.source_node.children.first.to_s
48
- self.source_node.children.first.children.first.to_s
87
+ def set_type_attribute(typestring)
88
+ Tools.set_ns_attribute(@xmlnode,'office','value-type',typestring)
49
89
  end
50
- def coordinates
51
- [row,col]
90
+
91
+ # based on @xmlnode and optionally value which is about to be assigned, guesses which type the result should be
92
+ def guess_cell_type(avalue=nil)
93
+ # try guessing by value
94
+ valueguess = case avalue
95
+ when Numeric then Float
96
+ when Date then Date
97
+ when String,nil then nil
98
+ else nil
99
+ end
100
+ result = valueguess
101
+
102
+ if valueguess.nil? # valueguess is most important
103
+ # if not succesfull then try guessing by type
104
+ type = @xmlnode.attributes['value-type'].to_s
105
+ typeguess = case type
106
+ when 'float' then Float
107
+ when 'string' then String
108
+ when 'date' then Date
109
+ when 'percentage' then 'percentage'
110
+ else
111
+ if @xmlnode.children.size == 0
112
+ nil
113
+ else
114
+ raise "Unknown type from #{@xmlnode.to_s} / children size=#{@xmlnode.children.size.to_s} / type=#{type}"
115
+ end
116
+ end
117
+
118
+ result =
119
+ if !typeguess.nil? # if not certain by value, but have a typeguess
120
+ if !avalue.nil? # with value we may try converting
121
+ if (typeguess(avalue) rescue false) # if convertible then it is typeguess
122
+ typeguess
123
+ elsif (String(avalue) rescue false) # otherwise try string
124
+ String
125
+ else # if not convertible to anyhing concious then nil
126
+ nil
127
+ end
128
+ else # without value we just beleive typeguess
129
+ typeguess
130
+ end
131
+ else # it not have a typeguess
132
+ if (String(avalue) rescue false) # convertible to String
133
+ String
134
+ else
135
+ nil
136
+ end
137
+ end
138
+ end
139
+ result
140
+ end
141
+ def inspect
142
+ "#<Cell:[#{row},#{col}]=#{value}(#{guess_cell_type.to_s})"
52
143
  end
53
144
  end
145
+
54
146
  end
147
+
148
+ # ## initialize cells
149
+ # @cells = Hash.new do |hash, coords|
150
+ # # we create empty cell and place it to hash, we do not have to check whether there is a cell in XML already, because it would be in hash as well
151
+ # hash[coords]=Cell.new(coords[0],coords[1])
152
+ # # TODO: create XML empty node here or upon save?
153
+ # end
154
+ # rowi = 1
155
+ # unless @xmlnode.nil?
156
+ # @xmlnode.elements.select{ |node| node.name == 'table-row'}.each do |row_source_node|
157
+ # coli = 1
158
+ # row_source_node.elements.select{ |node| node.name == 'table-cell'}.each do |cell_source_node|
159
+ # initialize_cell(rowi,coli,cell_source_node)
160
+ # coli += 1
161
+ # end
162
+ # rowi += 1
163
+ # end
164
+ # end
165
+
166
+