rspreadsheet 0.4.9 → 0.5.0

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
- SHA1:
3
- metadata.gz: e7334a14b9121a8cfefe62a271f865b0be259f32
4
- data.tar.gz: b76797efc38071ebc4d2120eb051e220f45e3657
2
+ SHA256:
3
+ metadata.gz: 345981780a0e704579af26d413e53cb49dc671fbdcef278ae1fbdcbccf2bcbdb
4
+ data.tar.gz: 24b298d8ccde36019e3fa06f5acfdc8e9370e909c5b621663b6b23ca7bc438fd
5
5
  SHA512:
6
- metadata.gz: 456b8b7240c21d4bcc6c4faa82775c46310517e26e7051025ddfb852bceff1375860d7c2b350e36fd92c183ba8019228d8e7ba2db7319d581a61f95b91118118
7
- data.tar.gz: a28c423ecb3a1e9b8644a7dfcf6c6f063a817d9a710edb80388a050e0738b0dd421783ab940987023725223af8d618df3753537b89c98478c169cf3da78e1f14
6
+ metadata.gz: 41fbd52d0c125cd5e4c6555b1de2dfc0360775986b73aff3e7eb947c17dfa03c0dbe85dadded72a00561e98ad40dca9e3052b2d42b2ea7746d818351e98933c1
7
+ data.tar.gz: 120436ad22dfd37afada167cb837172ebfe393940f58da85b5e6acfed127e275957f202a3a9e4e5ac5df7848baba3aa5554c11ddefa1b119960657ce6ef88805
data/GUIDE.md CHANGED
@@ -4,6 +4,7 @@
4
4
  You can open ODS file (OpenDocument Spreadsheet) like this
5
5
  ````ruby
6
6
  workbook = Rspreadsheet.open('./test.ods')
7
+ workbook = Rspreadsheet.open('./test.fods') # gem supports flast OpenDocument format
7
8
  ````
8
9
  and access its first sheet like this
9
10
  ````ruby
@@ -37,9 +38,24 @@ i.move_to('100mm','99.98mm')
37
38
  The file needs to be saved after doing changes.
38
39
  ````ruby
39
40
  workbook.save
40
- workbook.save('new_filename.ods') # changes filename and saves
41
- workbook.save(any_io_object) # file can be saved to any IO like object as well
41
+ workbook.save('new_filename.ods') # changes filename and saves
42
+ workbook.save(any_io_object) # file can be saved to any IO like object as well
43
+ workbook.to_io # coverts it to IO object which can be used to
44
+ anotherIO.write(workbook.to_io.read) # send file over internet without saving it first
42
45
  ````
46
+
47
+ ### Creating fresh new file
48
+ You may name the spreadsheet on creation or at first save.
49
+
50
+ ````ruby
51
+ workbook = Rspreadsheet.new
52
+ workbook.save('./filename.ods') # filename nust be provided at least on first save
53
+ workbook2 = Rspreadsheet.new('./filename2.fods', format: :flat)
54
+ workbook2.save
55
+ ```
56
+
57
+ If you want to use the fods flat format, you must create it as such.
58
+
43
59
  ### Date and Time
44
60
  OpenDocument and ruby have different models of date, time and datetime. Ruby containg three different objects. Time and DateTime cover all cases, Date covers dates only. OpenDocument distinguishes two groups - time of a day (time) and everything else (date). To simplify things a little we return cell values containg time of day as Time object and cell values containg datetime of date as DateTime. I am aware that this is very arbitrary choice, but it is very practical. This way and to some extend the types of values from OpenDocument are preserved when read from files, beeing acted upon and written back to spreadshhet.
45
61
 
data/README.md CHANGED
@@ -44,8 +44,9 @@ book.save
44
44
  book.save('different_filename.ods')
45
45
  ```
46
46
 
47
+ * Detailed [Guide to using Rspreadsheet](GUIDE.md) is available.
47
48
  * [More examples](https://gist.github.com/gorn/b432e6a69e82628349e6) of lots of alternative syntax **you can leave you comments and suggestions there**
48
- * [Guide to using Rspreadsheet](GUIDE.md) some other notes
49
+
49
50
 
50
51
  ## Installation and Configuration
51
52
 
@@ -63,15 +64,7 @@ Or install it yourself as:
63
64
 
64
65
  $ gem install rspreadsheet
65
66
 
66
- If you get this error concering libxml, like
67
-
68
- checking for libxml/xmlversion.h in /opt/include/libxml2,/opt/local/include/libxml2,/usr/local/include/libxml2,/usr/include/libxml2... no
69
- *** extconf.rb failed ***
70
- or
71
-
72
- mkmf.rb can't find header files for ruby at /usr/lib/ruby/include/ruby.h
73
-
74
- then you might not have installed libxml for ruby. I.e. in debian something like <code>sudo aptitude install ruby-libxml</code> or using equivalent command in other package managers.
67
+ If you get any error, have a look at [troubleshooting](TROUBLESHOOTING.md)
75
68
 
76
69
  ## Contibutions, ideas and wishes welcomed
77
70
 
@@ -0,0 +1,25 @@
1
+ # Troubleshooting
2
+
3
+ ## Installation
4
+
5
+ ### libxml
6
+
7
+ The gem detects natively installed libxml and uses instead of installing new one from rubygems. There might be some errors connected to this. If you get this error concering libxml, like
8
+
9
+ checking for libxml/xmlversion.h in /opt/include/libxml2,/opt/local/include/libxml2,/usr/local/include/libxml2,/usr/include/libxml2... no
10
+ *** extconf.rb failed ***
11
+ or
12
+
13
+ mkmf.rb can't find header files for ruby at /usr/lib/ruby/include/ruby.h
14
+
15
+ then you might not have installed libxml for ruby. I.e. in debian something like
16
+
17
+ sudo aptitude install ruby-libxml
18
+
19
+ or using equivalent command in other package managers.
20
+
21
+ ### if you do NOT want to use netively installed libxml
22
+
23
+ If you want to use libxml from rubygems, than uninstall the native one or add this line to your Gemfile
24
+
25
+ 'libxml-ruby', '3.0'
@@ -14,7 +14,7 @@ if RUBY_VERSION > '2.1'
14
14
 
15
15
  refine LibXML::XML::Node do
16
16
  def equals?(node2); raise 'nic' end
17
- def ==(node2)
17
+ def ===(node2)
18
18
  self.simplification_of?(node2) and node2.simplification_of?(self)
19
19
  end
20
20
  # if node2 contains at least all that I do
@@ -31,15 +31,15 @@ if RUBY_VERSION > '2.1'
31
31
  return "#{where}> Attribute #{attr} have diffent values: #{attr.value} != #{node2.attributes[attr.name]}" unless node2.attributes[attr.name] == attr.value
32
32
  end
33
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)
34
+ elems1 = self.children
35
+ elems2 = node2.children
36
+ return "#{where}> elements have different number of children #{elems1.length} != #{elems2.length}" if (elems1.length != elems2.length)
37
37
 
38
38
  elems1.each_index do |i|
39
39
  raise "Nil for i=#{i}" if elems1[i].nil?
40
40
  if (elems1[i].node_type_name == 'text')
41
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}"
42
+ return "#{where}> #{i+1}th text children are different: #{elems1[i].to_s} != #{elems2[i].to_s}"
43
43
  end
44
44
  elsif (elems1[i].node_type_name == 'element')
45
45
  unless elems1[i].simplification_of?(elems2[i])
@@ -72,7 +72,7 @@ else # Monkeypatching
72
72
  end
73
73
 
74
74
  class LibXML::XML::Node
75
- def ==(node2)
75
+ def ===(node2)
76
76
  self.simplification_of?(node2) and node2.simplification_of?(self)
77
77
  end
78
78
  # if node2 contains at least all that I do
@@ -88,15 +88,21 @@ else # Monkeypatching
88
88
  self.attributes.each do |attr|
89
89
  return "#{where}> Attribute #{attr} have diffent values: #{attr.value} != #{node2.attributes[attr.name]}" unless node2.attributes[attr.name] == attr.value
90
90
  end
91
+
92
+ elems1 = self.children
93
+ elems2 = node2.children
94
+ return "#{where}> elements have different number of children #{elems1.length} != #{elems2.length}" if (elems1.length != elems2.length)
91
95
 
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])}"
96
+ elems1.each_index do |i|
97
+ raise "Nil for i=#{i}" if elems1[i].nil?
98
+ if (elems1[i].node_type_name == 'text')
99
+ if elems2[i].nil? || (elems1[i].to_s) != (elems2[i].to_s)
100
+ return "#{where}> #{i+1}th text children are different: #{elems1[i].to_s} != #{elems2[i].to_s}"
101
+ end
102
+ elsif (elems1[i].node_type_name == 'element')
103
+ unless elems1[i].simplification_of?(elems2[i])
104
+ return "#{where}/[#{i+1}]#{elems1[i].first_diff(elems2[i])}"
105
+ end
100
106
  end
101
107
  end
102
108
 
@@ -113,4 +119,4 @@ else # Monkeypatching
113
119
  # end
114
120
  end
115
121
 
116
- end
122
+ end
@@ -7,13 +7,36 @@ require 'helpers/class_extensions'
7
7
 
8
8
  module Rspreadsheet
9
9
  extend Configuration
10
+
10
11
  define_setting :raise_on_negative_coordinates, true
11
12
 
12
13
  # makes creating new workbooks as easy as `Rspreadsheet.new` or `Rspreadsheet.open('filename.ods')
13
- def self.new(filename=nil)
14
- Workbook.new(filename)
14
+ def self.new(*params)
15
+ raise ArgumentError.new("wrong number of arguments (given #{params.size}, expected 0-2)") if params.size >2
16
+
17
+ case params.last
18
+ when Hash then options = params.pop
19
+ else options = {}
20
+ end
21
+
22
+ if options[:format].nil? # automatické heuristické rozpoznání formátu
23
+ options[:format] = :standard
24
+ unless params.first.nil?
25
+ begin
26
+ Zip::File.open(params.first)
27
+ rescue
28
+ options[:format] = :flat
29
+ end
30
+ end
31
+ end
32
+
33
+ case options[:format]
34
+ when :flat , :fods then WorkbookFlat.new(*params)
35
+ when :standard then Workbook.new(*params)
36
+ else raise 'format of the file not recognized'
37
+ end
15
38
  end
16
- def self.open(filename)
17
- Workbook.new(filename)
39
+ def self.open(filename, options = {})
40
+ self.new(filename, options)
18
41
  end
19
42
  end
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:presentation="urn:oasis:names:tc:opendocument:xmlns:presentation:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.spreadsheet">
4
+ <office:meta><meta:generator>Rspreadshhet gem by Gorn</meta:generator></office:meta>
5
+ <office:automatic-styles />
6
+ <office:body>
7
+ <office:spreadsheet />
8
+ </office:body>
9
+ </office:document>
@@ -1,7 +1,11 @@
1
- module Rspreadsheet
1
+ require 'pry'
2
+
3
+ module Rspreadsheet
2
4
 
3
5
  # this module contains methods used bz several objects
4
6
  module Tools
7
+ using ClassExtensions if RUBY_VERSION > '2.1'
8
+
5
9
  def self.only_letters?(x); x.kind_of?(String) and x.match(/^[A-Za-z]*$/) != nil end
6
10
  def self.kind_of_integer?(x)
7
11
  (x.kind_of?(Numeric) and x.to_i==x) or
@@ -162,7 +166,8 @@ module Tools
162
166
  end
163
167
  end
164
168
  def self.remove_ns_attribute(node,ns_prefix,key)
165
- node.attributes.get_attribute_ns(Tools.get_namespace(ns_prefix).href,key)
169
+ ns = Tools.get_namespace(ns_prefix)
170
+ attr = node.attributes.get_attribute_ns(ns.href, key)
166
171
  attr.remove! unless attr.nil?
167
172
  end
168
173
  def self.prepare_ns_node(ns_prefix,nodename,value=nil)
@@ -176,7 +181,6 @@ module Tools
176
181
  end
177
182
  end
178
183
 
179
-
180
184
  def self.get_unused_filename(zip,prefix, extension)
181
185
  (1000..9999).each do |ndx|
182
186
  filename = prefix + ndx.to_s + ((Time.now.to_r*1000000000).to_i.to_s(16)) + extension
@@ -188,7 +192,45 @@ module Tools
188
192
  def self.new_time_value(h,m,s)
189
193
  Time.new(StartOfEpoch.year,StartOfEpoch.month,StartOfEpoch.day,h,m,s)
190
194
  end
195
+
196
+ def self.output_to_zip_stream(io,&block)
197
+ if io.kind_of? File or io.kind_of? String
198
+ Zip::File.open(io, 'br+') do |zip|
199
+ yield zip
200
+ end
201
+ elsif io.kind_of? StringIO # or io.kind_of? IO
202
+ Zip::OutputStream.write_buffer(io) do |zip|
203
+ yield zip
204
+ end
205
+ end
206
+ end
191
207
 
208
+ def self.content_xml_diff(filename1,filename2)
209
+ content_xml1 = Zip::File.open(filename1) do |zip|
210
+ LibXML::XML::Document.io zip.get_input_stream('content.xml')
211
+ end
212
+ content_xml2 = Zip::File.open(filename2) do |zip|
213
+ LibXML::XML::Document.io zip.get_input_stream('content.xml')
214
+ end
215
+
216
+ return xml_diff(content_xml1.root,content_xml2.root)
217
+ end
218
+
219
+ def self.xml_file_diff(filename1,filename2)
220
+ content_xml1 = LibXML::XML::Document.file(filename1).root
221
+ content_xml2 = LibXML::XML::Document.file(filename2).root
222
+ return xml_diff(content_xml1, content_xml2)
223
+ end
224
+
225
+ def self.xml_diff(xml_node1,xml_node2)
226
+ message = []
227
+ message << xml_node2.first_diff(xml_node1)
228
+ message << xml_node1.first_diff(xml_node2)
229
+ message << 'content XML not equal' unless xml_node1.to_s.should == xml_node2.to_s
230
+ message = message.compact.join('; ')
231
+ message = nil if message == ''
232
+ message
233
+ end
192
234
  end
193
235
 
194
236
  end
@@ -1,3 +1,3 @@
1
1
  module Rspreadsheet
2
- VERSION = "0.4.9"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -2,12 +2,13 @@ require 'zip'
2
2
  require 'libxml'
3
3
 
4
4
  module Rspreadsheet
5
+
5
6
  class Workbook
6
7
  attr_reader :filename
7
8
  attr_reader :xmlnode # debug
8
9
  def xmldoc; @xmlnode.doc end
9
10
 
10
- #@!group Worskheets methods
11
+ #@!group Worksheet methods
11
12
  def create_worksheet_from_node(source_node)
12
13
  sheet = Worksheet.new(source_node,self)
13
14
  register_worksheet(sheet)
@@ -21,10 +22,11 @@ class Workbook
21
22
  alias :add_worksheet :create_worksheet
22
23
  # @return [Integer] number of sheets in the workbook
23
24
  def worksheets_count; @worksheets.length end
25
+ alias :worksheet_count :worksheets_count
24
26
  # @return [String] names of sheets in the workbook
25
27
  def worksheet_names; @worksheets.collect{ |ws| ws.name } end
26
28
  # @param [Integer,String]
27
- # @return [Worskheet] worksheet with given index or name
29
+ # @return [Worksheet] worksheet with given index or name
28
30
  def worksheets(index_or_name)
29
31
  case index_or_name
30
32
  when Integer then begin
@@ -61,8 +63,7 @@ class Workbook
61
63
  @xmlnode.find('./table:table').each do |node|
62
64
  create_worksheet_from_node(node)
63
65
  end
64
- end
65
-
66
+ end
66
67
 
67
68
  # @param [String] Optional new filename
68
69
  # Saves the worksheet. Optionally you can provide new filename or IO stream to which the file should be saved.
@@ -70,47 +71,53 @@ class Workbook
70
71
  case
71
72
  when @filename.nil? && io.nil?
72
73
  raise 'New file should be named on first save.'
73
- when @filename.nil? && (io.kind_of?(String) || io.kind_of?(File) || io.kind_of?(IO) || io.kind_of?(StringIO))
74
- Zip::File.open(TEMPLATE_FILE_NAME) do |empty_template_zip| # open empty_template file
75
- write_zip_to(io) do |output_zip| # open output stream of file
76
- copy_internally_without_content(empty_template_zip,output_zip) # copy empty_template internals
77
- update_manifest_and_content_xml(empty_template_zip,output_zip) # update xmls + pictures
78
- end
79
- end
80
74
  when @filename.kind_of?(String) && io.nil?
81
- write_zip_to(@filename) do |input_and_output_zip| # open old file
82
- update_manifest_and_content_xml(input_and_output_zip,input_and_output_zip) # input and output are identical
75
+ Tools.output_to_zip_stream(@filename) do |input_and_output_zip| # open old file
76
+ update_zip_manifest_and_content_xml(input_and_output_zip,input_and_output_zip) # input and output are identical
83
77
  end
84
-
85
- when @filename.kind_of?(String) && (io.kind_of?(String) || io.kind_of?(File))
86
- io = io.path if io.kind_of?(File) # convert file to its filename
87
- FileUtils.cp(@filename , io) # copy file externally
88
- @filename = io # remember new name
89
- save_to_io(nil) # continue modyfying file on spot
90
-
91
- when @filename.kind_of?(String) && (io.kind_of?(IO) || io.kind_of?(StringIO))
92
- Zip::File.open(@filename) do | old_zip | # open old file
93
- write_zip_to(io) do |output_zip_stream| # open output stream
94
- copy_internally_without_content(old_zip,output_zip_stream) # copy the old internals
95
- update_manifest_and_content_xml(old_zip,output_zip_stream) # update xmls + pictures
96
- end
78
+ when (@filename.kind_of?(String) && (io.kind_of?(String) || io.kind_of?(File)))
79
+ io = io.path if io.kind_of?(File) # convert file to its filename
80
+ FileUtils.cp(@filename , io) # copy file externally
81
+ @filename = io # remember new name
82
+ save_to_io(nil) # continue modyfying file on spot
83
+ when io.kind_of?(IO) || io.kind_of?(String) || io.kind_of?(StringIO)
84
+ Tools.output_to_zip_stream(io) do |output_io| # open output stream of file
85
+ write_ods_to_io(output_io)
97
86
  end
98
- # rewind result
99
- io.rewind
100
- else
87
+ io.rewind if io.kind_of?(StringIO)
88
+ else raise 'Ivalid combinations of parameter types in save'
101
89
  end
102
90
  end
103
- alias :to_io :save
104
91
  alias :save_to_io :save
92
+ alias :save_as :save
93
+ def to_io
94
+ WorkbookIO.new(self)
95
+ end
96
+
97
+ def write_ods_to_io(io)
98
+ if @filename.nil?
99
+ Zip::File.open(TEMPLATE_FILE_NAME) do |empty_template_zip| # open empty_template file
100
+ copy_internally_without_content(empty_template_zip,io) # copy empty_template internals
101
+ update_zip_manifest_and_content_xml(empty_template_zip,io) # update xmls + pictures
102
+ end
103
+ else
104
+ Zip::File.open(@filename) do | old_zip | # open old file
105
+ copy_internally_without_content(old_zip,io) # copy the old internals
106
+ update_zip_manifest_and_content_xml(old_zip,io) # update xmls + pictures
107
+ end
108
+ end
109
+ end
105
110
 
111
+ def flat_format?; false end
112
+ def normal_format?; true end
113
+
106
114
  private
107
115
 
108
- def update_manifest_and_content_xml(input_zip,output_zip)
116
+ def update_zip_manifest_and_content_xml(input_zip,output_zip)
109
117
  update_manifest_xml(input_zip,output_zip)
110
118
  update_content_xml(output_zip)
111
119
  end
112
120
 
113
-
114
121
  def update_content_xml(zip)
115
122
  save_entry_to_zip(zip,CONTENT_FILE_NAME,@content_xml.to_s(indent: false))
116
123
  end
@@ -162,7 +169,6 @@ class Workbook
162
169
 
163
170
  def save_entry_to_zip(zip,internal_filename,contents)
164
171
  if zip.kind_of? Zip::File
165
- # raise [internal_filename,contents].inspect unless File.exists?(internal_filename)
166
172
  zip.get_output_stream(internal_filename) do |f|
167
173
  f.write contents
168
174
  end
@@ -171,19 +177,7 @@ class Workbook
171
177
  zip.write(contents)
172
178
  end
173
179
  end
174
-
175
- def write_zip_to(io,&block)
176
- if io.kind_of? File or io.kind_of? String
177
- Zip::File.open(io, 'br+') do |zip|
178
- yield zip
179
- end
180
- elsif io.kind_of? StringIO # or io.kind_of? IO
181
- Zip::OutputStream.write_buffer(io) do |zip|
182
- yield zip
183
- end
184
- end
185
- end
186
-
180
+
187
181
  CONTENT_FILE_NAME = 'content.xml'
188
182
  MANIFEST_FILE_NAME = 'META-INF/manifest.xml'
189
183
  TEMPLATE_FILE_NAME = (File.dirname(__FILE__)+'/empty_file_template.ods').freeze
@@ -192,5 +186,59 @@ class Workbook
192
186
  @worksheets[index-1]=worksheet
193
187
  @xmlnode << worksheet.xmlnode if worksheet.xmlnode.doc != @xmlnode.doc
194
188
  end
189
+
190
+ end
191
+
192
+ class WorkbookFlat < Workbook
193
+ def initialize(afilename=nil)
194
+ @worksheets=[]
195
+ @filename = afilename
196
+ @xml_doc = LibXML::XML::Document.file(@filename || FLAT_TEMPLATE_FILE_NAME)
197
+ @xmlnode = @xml_doc.find_first('//office:spreadsheet')
198
+ @xmlnode.find('./table:table').each do |node|
199
+ create_worksheet_from_node(node)
200
+ end
201
+ end
202
+
203
+ def save(io=nil)
204
+ case
205
+ when @filename.nil? && io.nil?
206
+ raise 'New file should be named on first save, please provide filename (or IO).'
207
+ when @filename.kind_of?(String) && io.nil?
208
+ @xml_doc.save(@filename)
209
+ when (@filename.kind_of?(String) && (io.kind_of?(String) || io.kind_of?(File)))
210
+ @filename = (io.kind_of?(File)) ? io.path : io
211
+ @xml_doc.save(@filename)
212
+ when io.kind_of?(IO) || io.kind_of?(String) || io.kind_of?(StringIO)
213
+ IO.write(io,@xml_doc.to_s)
214
+ io.rewind if io.kind_of?(StringIO)
215
+ else raise 'Invalid combinations of parameter types in save'
216
+ end
217
+ end
218
+ alias :save_to_io :save
219
+ alias :save_as :save
220
+
221
+ def flat_format?; true end
222
+ def normal_format?; false end
223
+
224
+ private
225
+ FLAT_TEMPLATE_FILE_NAME = (File.dirname(__FILE__)+'/empty_file_template.fods').freeze
226
+
227
+ end
228
+
229
+ class WorkbookIO
230
+ def initialize(workbook)
231
+ @workbook = workbook
232
+ end
233
+ def read
234
+ buffer.string
235
+ end
236
+ private
237
+ def buffer
238
+ Zip::OutputStream.write_buffer do |io|
239
+ @workbook.write_ods_to_io(io)
240
+ end
241
+ end
195
242
  end
243
+
196
244
  end