rspreadsheet 0.4.9 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
 - data/GUIDE.md +18 -2
 - data/README.md +3 -10
 - data/TROUBLESHOOTING.md +25 -0
 - data/lib/helpers/class_extensions.rb +21 -15
 - data/lib/rspreadsheet.rb +27 -4
 - data/lib/rspreadsheet/empty_file_template.fods +9 -0
 - data/lib/rspreadsheet/tools.rb +45 -3
 - data/lib/rspreadsheet/version.rb +1 -1
 - data/lib/rspreadsheet/workbook.rb +94 -46
 - data/lib/rspreadsheet/worksheet.rb +0 -4
 - data/spec/cell_spec.rb +6 -1
 - data/spec/class_extensions_spec.rb +16 -3
 - data/spec/fods_spec.rb +70 -0
 - data/spec/rspreadsheet_spec.rb +22 -4
 - data/spec/spec_helper.rb +1 -3
 - data/spec/testfile1.fods +1039 -0
 - data/spec/testfile1.ods +0 -0
 - data/spec/tools_spec.rb +13 -0
 - data/spec/worksheet_spec.rb +14 -0
 - metadata +19 -7
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 2 
     | 
    
         
            +
            SHA256:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 345981780a0e704579af26d413e53cb49dc671fbdcef278ae1fbdcbccf2bcbdb
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 24b298d8ccde36019e3fa06f5acfdc8e9370e909c5b621663b6b23ca7bc438fd
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 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') 
     | 
| 
       41 
     | 
    
         
            -
            workbook.save(any_io_object) 
     | 
| 
      
 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 
     | 
    
         
            -
             
     | 
| 
      
 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  
     | 
| 
       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 
     | 
    
         | 
    
        data/TROUBLESHOOTING.md
    ADDED
    
    | 
         @@ -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  
     | 
| 
      
 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. 
     | 
| 
       35 
     | 
    
         
            -
                    elems2 = node2. 
     | 
| 
       36 
     | 
    
         
            -
                    return "#{where}> elements have different number of  
     | 
| 
      
 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  
     | 
| 
      
 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  
     | 
| 
      
 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  
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
     | 
    
         
            -
             
     | 
| 
       95 
     | 
    
         
            -
             
     | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
       97 
     | 
    
         
            -
                       
     | 
| 
       98 
     | 
    
         
            -
                    elsif (elems1[i].node_type_name == 'element')  
     | 
| 
       99 
     | 
    
         
            -
                       
     | 
| 
      
 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
         
     | 
    
        data/lib/rspreadsheet.rb
    CHANGED
    
    | 
         @@ -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( 
     | 
| 
       14 
     | 
    
         
            -
                 
     | 
| 
      
 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 
     | 
    
         
            -
                 
     | 
| 
      
 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>
         
     | 
    
        data/lib/rspreadsheet/tools.rb
    CHANGED
    
    | 
         @@ -1,7 +1,11 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
             
     | 
| 
      
 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 
     | 
    
         
            -
                 
     | 
| 
      
 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
         
     | 
    
        data/lib/rspreadsheet/version.rb
    CHANGED
    
    
| 
         @@ -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  
     | 
| 
      
 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 [ 
     | 
| 
      
 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 
     | 
    
         
            -
                     
     | 
| 
       82 
     | 
    
         
            -
                       
     | 
| 
      
 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 
     | 
    
         
            -
             
     | 
| 
       86 
     | 
    
         
            -
                     
     | 
| 
       87 
     | 
    
         
            -
                     
     | 
| 
       88 
     | 
    
         
            -
                     
     | 
| 
       89 
     | 
    
         
            -
             
     | 
| 
       90 
     | 
    
         
            -
                    
         
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       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 
     | 
    
         
            -
                     
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       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  
     | 
| 
      
 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
         
     |