energon 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/MIT-LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (c) 2006 Woa! Kft <energon@woa.hu>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,31 @@
1
+ =Energon
2
+
3
+
4
+ Energon is a report engine written in ruby. It allows you to
5
+ create reliable and advanced reports from your templates.
6
+ Both templates and output documents are based on OpenXML and OpenDocument formats.
7
+
8
+ Energon supports currently Text, Word and Excel documents.
9
+
10
+ 1. Create a template of your choice (Text, Word, Excel).
11
+ 2. Add placeholders into your document, those placeholders will be replaced later by some data. Note that you can add some formatting instructions on your placeholders.
12
+ 3. Merge your data and your template. Data source is a simple ruby Hash, and thus can be created for instance from some ActiveRecord data.
13
+ 4. Save your output, it's all that simple!
14
+
15
+ ===Installation
16
+ To install the lastest Energon, just type:
17
+ gem install energon
18
+
19
+ ===Changes
20
+ 0.0.1: First release
21
+
22
+ ===License
23
+ Energon is released under the MIT License
24
+
25
+ ===Woa! Kft
26
+
27
+ Woa! Kft is a hungarian based company, and provides top-notch Rails development at affordable prices for individuals and small to medium size businesses.
28
+
29
+ ===Support
30
+ Any questions, enhancement proposals, bug notifications or corrections can be sent to mailto:energon@woa.hu.
31
+ You can visit our website: http://www.woa.hu.
@@ -0,0 +1,105 @@
1
+ require 'energon/parser'
2
+
3
+ module Woa; module Energon
4
+ class EnergonError < StandardError #:nodoc:
5
+ end
6
+ class NoPlaceholderFound < StandardError #:nodoc:
7
+ end
8
+
9
+ # This class is the fronthead class for Energon. It's the main class
10
+ # and it executes the hight level stuffs. It deals with text files. Subclasses deal with
11
+ # other kind of templates:
12
+ # * ExcelDocument for Excel templates
13
+ # * WordDocument for Word templates
14
+ # * OdDocument for OpenDocument (OpenOffice write and spreadsheet)
15
+ #
16
+ # :include: rdoc-header
17
+ #
18
+ # == Examples
19
+ #
20
+ # require 'energon'
21
+ # include Woa::Energon
22
+ # document = Document.new("@author@, @date@, @country@")
23
+ # document.add_values({:author => "Woa! Kft", :date => Time.now})
24
+ # document.add_value(:country => "Hungary")
25
+ # puts document.write #"Woa! Kft, Wed Nov 29 11:31:52 CET 2006, Hungary"
26
+ class Document
27
+
28
+ DefaultDelimiter = '@'
29
+ DefaultNewLine = "\n"
30
+
31
+ attr_accessor :delimiter
32
+ attr_accessor :new_line
33
+
34
+ # Just pass the template and specify if the delimiter is not the one by default ("@")
35
+ def initialize(template, delimiter=DefaultDelimiter)
36
+ raise EnergonError if delimiter.to_s.empty?
37
+ @delimiter = delimiter.to_s
38
+ @new_line = DefaultNewLine
39
+ @datas = {}
40
+ @template = template
41
+ @placeholders = []
42
+ extract_placeholders
43
+ end
44
+
45
+ # Add a data
46
+ # * key is the name of the data
47
+ # * value is the data
48
+ def add_value(key, value)
49
+ @datas[key] = value
50
+ end
51
+
52
+ # Add several datas from a Hash.
53
+ # * the key is the name of the data
54
+ # * the value is the data
55
+ def add_values(hash)
56
+ hash.each {|key, value| self.add_value(key, value)}
57
+ end
58
+
59
+ # Is the template valid ? It's valid when
60
+ # * there is at least one data
61
+ # * there is at least one placeholder in the template
62
+ # * there is at least one data for each placeholder
63
+ #
64
+ # It will return true or false.
65
+ def valid?
66
+ raise NoPlaceholderFound if @placeholders.empty?
67
+ @placeholders.each do |placeholder|
68
+ begin
69
+ Parser.merge(placeholder, @datas)
70
+ rescue NoDataFound
71
+ return(false)
72
+ end
73
+ end
74
+ true
75
+ end
76
+
77
+ # Generate the final document.
78
+ # Raise an exception if an error occurs
79
+ #
80
+ # It's possible to specify the ouput to write directly in a specific stream.
81
+ def write(output=nil)
82
+ raise NoPlaceholderFound if @placeholders.empty?
83
+ @placeholders.each do |placeholder|
84
+ data = Parser.merge(placeholder, @datas)
85
+ data = data.join(@new_line) if data.is_a?(Array)
86
+ @template.gsub!("#{@delimiter}#{placeholder}#{@delimiter}", data)
87
+ end
88
+ return(@template) if output.nil?
89
+ output.puts @template
90
+ end
91
+
92
+ alias :close :write
93
+
94
+ #########
95
+ private #
96
+ #########
97
+
98
+ def extract_placeholders
99
+ # the regexp is : /@((\@|[^@])*)@/
100
+ @template.scan(Regexp.new("#{@delimiter}((#{Regexp.escape('\\' + @delimiter)}|[^#{@delimiter}])*)#{@delimiter}")) do |match, non_used|
101
+ @placeholders << match unless @placeholders.include?(match)
102
+ end
103
+ end
104
+ end
105
+ end; end
@@ -0,0 +1,138 @@
1
+ require 'energon/document'
2
+ require 'energon/open_xml_helper'
3
+ require 'rexml/document'
4
+ include REXML
5
+
6
+ module Woa; module Energon;
7
+ # This is a subclass of Document but it deals with Excel templates
8
+ # instead of Text templates.
9
+ #
10
+ # Everything is the same as Document (Except the input and the output).
11
+ # See Document for more details.
12
+ #
13
+ # :include: rdoc-header
14
+ class ExcelDocument < Document
15
+
16
+ def write()
17
+ raise NoPlaceholderFound if @placeholders.empty?
18
+ rows = {}
19
+ @placeholders.each do |placeholder_hash|
20
+ placeholder = placeholder_hash[:placeholder]
21
+ data = Parser.merge(placeholder, @datas)
22
+ if data.is_a?(Array)
23
+ placeholder_hash[:cells].each do |cell|
24
+ parent = cell
25
+ begin
26
+ next unless parent.name == "row"
27
+ rows[parent] = [] if rows[parent].nil?
28
+ rows[parent] << {:cell => cell, :data => data}
29
+ break
30
+ end while (parent = parent.parent)
31
+ end
32
+ else
33
+ element = placeholder_hash[:shared_string_element]
34
+ element.text = data
35
+ end
36
+ end
37
+ inserted_rows = -1
38
+ rows.each do |row, cells|
39
+ last_row = row
40
+ index = row.attributes.get_attribute('r').value.to_i
41
+ continue = true
42
+ while continue
43
+ cells.each do |element|
44
+ datas = element[:data]
45
+ cell = element[:cell]
46
+ data = datas.pop
47
+ if data.nil?
48
+ continue = false
49
+ break
50
+ end
51
+ cell.text = add_shared_string(data.to_s)
52
+ end
53
+ if continue
54
+ new_row = row.deep_clone
55
+ new_row.add_attribute('r', index.to_s)
56
+ XPath.each(new_row, '//c[@r]') do |element|
57
+ value = element.attributes.get_attribute('r').value.to_s.gsub(/\d+/, index.to_s)
58
+ element.add_attribute('r', value)
59
+ end
60
+ index = index.next
61
+ row.parent.insert_after(last_row, new_row)
62
+ last_row = new_row
63
+ inserted_rows = inserted_rows.next
64
+ end
65
+ end
66
+ root = row.parent
67
+ initial_index = row.attributes.get_attribute('r').value.to_i
68
+ row.parent.delete(row)
69
+ index = last_row.parent.index(last_row)
70
+ XPath.each(root, 'row') do |element|
71
+ next if element.parent.index(element) <= index
72
+ value = element.attributes.get_attribute('r').value.to_i + inserted_rows
73
+ element.add_attribute('r', value)
74
+ XPath.each(element, 'c[@r]') do |element|
75
+ new_value = element.attributes.get_attribute('r').value.to_s.gsub(/\d+/, value.to_s)
76
+ element.add_attribute('r', new_value)
77
+ end
78
+ end
79
+ XPath.each(root, '//mergeCell[@ref]') do |element|
80
+ value = element.attributes.get_attribute('ref').value.to_s
81
+ if /([A-Z]+)(\d+):([A-Z]+)(\d+)/ =~ value
82
+ v1, v2, v3, v4 = Regexp.last_match[1..4]
83
+ v2 = v2.to_i + inserted_rows if v2.to_i > initial_index
84
+ v4 = v4.to_i + inserted_rows if v4.to_i > initial_index
85
+ element.add_attribute('ref', "#{v1}#{v2}:#{v3}#{v4}")
86
+ end
87
+ end
88
+ end
89
+ @documents.each {|document| @openxml.save(document) }
90
+ @openxml.write
91
+ end
92
+ alias :close :write
93
+
94
+ #############################
95
+ private
96
+ #############################
97
+ def extract_placeholders
98
+ @openxml = OpenXmlHelper.new_excel(@template)
99
+ @documents = @openxml.documents
100
+
101
+ # cells
102
+ cells = {}
103
+ @openxml.worksheets.each do |worksheet|
104
+ XPath.each(worksheet, '//c[@t="s"]/v') do |cell|
105
+ index = cell.text
106
+ cells[index] = [] if cells[index].nil?
107
+ cells[index] << cell
108
+ end
109
+ end
110
+
111
+ # shared strings
112
+ @shared_strings_xml = @openxml.shared_strings
113
+ raise NoPlaceholderFound if @shared_strings_xml.nil?
114
+ @shared_strings = []
115
+ XPath.each(@shared_strings_xml, "//si/t") do |element|
116
+ index = element.parent.parent.index(element.parent)
117
+ @shared_strings << element
118
+ element.text.to_s.scan(Regexp.new("#{@delimiter}((#{Regexp.escape('\\' + @delimiter)}|[^#{@delimiter}])*)#{@delimiter}")) do |match, non_used|
119
+ @placeholders << {:placeholder => match, :shared_string_element => element, :index => index, :cells => cells[index.to_s].nil? ? [] : cells[index.to_s]}
120
+ end
121
+ end
122
+ end
123
+
124
+ def add_shared_string(str)
125
+ root = @shared_strings_xml.root
126
+ t = Element.new('t')
127
+ t.text = str
128
+ si = Element.new('si')
129
+ si << t
130
+ root << si
131
+ count = root.attributes.get_attribute('count').value.to_i.next
132
+ unique_count = root.attributes.get_attribute('uniqueCount').value.to_i.next
133
+ root.add_attribute('count', count.to_s)
134
+ root.add_attribute('uniqueCount', unique_count.to_s)
135
+ count - 1
136
+ end
137
+ end
138
+ end; end
@@ -0,0 +1,90 @@
1
+ require 'energon/document'
2
+ require 'energon/open_document_helper'
3
+ require 'rexml/document'
4
+ include REXML
5
+
6
+ module Woa; module Energon;
7
+ # This is a subclass of Document but it deals with OpenDocument
8
+ # (OpenOffice) templates instead of Text templates. It works
9
+ # with the writer and the spreadsheet files.
10
+ #
11
+ # Everything is the same as Document (Except the input and the output).
12
+ # See Document for more details.
13
+ #
14
+ # :include: rdoc-header
15
+ class OdDocument < Document
16
+ def write()
17
+ raise NoPlaceholderFound if @placeholders.empty?
18
+ rows = {}
19
+
20
+
21
+ @placeholders.each do |placeholder_hash|
22
+ placeholder = placeholder_hash[:placeholder]
23
+ data = Parser.merge(placeholder, @datas)
24
+ if data.is_a?(Array)
25
+ element = placeholder_hash[:element]
26
+
27
+ parent = XPath.first(element, "ancestor::table:table-row")
28
+
29
+ # if not in a table
30
+ if parent.nil?
31
+ element.text = element.text.to_s.sub("#{delimiter}#{placeholder}#{delimiter}", data.join)
32
+ else
33
+ rows[parent] ||= []
34
+ rows[parent] << {:element => element, :data => data, :placeholder => placeholder}
35
+ end
36
+ else
37
+ element = placeholder_hash[:element]
38
+ element.text = element.text.to_s.sub("#{delimiter}#{placeholder}#{delimiter}", data)
39
+ end
40
+ end
41
+
42
+
43
+ originals = {}
44
+ rows.each do |row, elements|
45
+
46
+ continue = true
47
+ while continue
48
+
49
+ originals.each do |element, text|
50
+ element.text = text
51
+ originals.delete(element)
52
+ end
53
+
54
+ elements.each do |hash|
55
+ element = hash[:element]
56
+ data = hash[:data]
57
+ placeholder = hash[:placeholder]
58
+
59
+ if data.empty?
60
+ continue = false
61
+ break
62
+ end
63
+
64
+ originals[element] = element.text.to_s.clone
65
+ txt = element.text = element.text.to_s.sub("#{delimiter}#{placeholder}#{delimiter}", data.pop)
66
+ end
67
+ row.parent.insert_after(row, row.deep_clone) if continue
68
+ end
69
+ row.parent.delete_element(row)
70
+ end
71
+
72
+ @odt.save(@content)
73
+ @odt.write
74
+ end
75
+ alias :close :write
76
+
77
+ #############################
78
+ private
79
+ #############################
80
+ def extract_placeholders
81
+ @odt = OpenDocumentHelper.new(@template)
82
+ @content = @odt.content
83
+ XPath.each(@content, "//text:p") do |element|
84
+ element.text.to_s.scan(Regexp.new("#{@delimiter}((#{Regexp.escape('\\' + @delimiter)}|[^#{@delimiter}])*)#{@delimiter}")) do |match, non_used|
85
+ @placeholders << {:placeholder => match, :element => element}
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end; end
@@ -0,0 +1,65 @@
1
+ require 'fileutils'
2
+ require 'rexml/document'
3
+ require 'iconv'
4
+ require 'rubygems'
5
+ require 'zip/zipfilesystem'
6
+ include Zip
7
+
8
+ module Woa; module Energon
9
+ class InvalidODFDocument < StandardError
10
+ end
11
+
12
+ # This class is an interface with OpenDocument files.
13
+ # It manages the content of the file so that caller classes
14
+ # just manage xml Objects (ReXml). It's not possible at this
15
+ # time to work with streams beacause of the zip library
16
+ # (rubyzip), it has to be done via files.
17
+ #
18
+ # :include: rdoc-header
19
+ class OpenDocumentHelper
20
+
21
+ attr_reader :type, :content
22
+
23
+ # * template: the name of the file
24
+ def initialize(template)
25
+ @zip_file = ZipFile.open(template)
26
+ @content = xml(read("content.xml")) rescue nil
27
+ raise InvalidODFDocument if @content.nil?
28
+ end
29
+
30
+ # Save the file content.xml
31
+ # * content: the xml Object (ReXml)
32
+ def save(content)
33
+ @content = content
34
+ save_xml(@content, "content.xml");
35
+ end
36
+
37
+ # Write the final document (the zipfile) and close the file
38
+ def write
39
+ @zip_file.close
40
+ end
41
+
42
+ alias :close :write
43
+
44
+ #########################################
45
+ private
46
+ #########################################
47
+
48
+ # Save a xml file
49
+ def save_xml(xml, file)
50
+ @zip_file.file.open(file, 'w') do |f|
51
+ f.write(xml.to_s.unpack('C*').pack('U*'))
52
+ end
53
+ end
54
+
55
+ # return the REXML::Document from a file
56
+ def xml(file)
57
+ REXML::Document.new(file)
58
+ end
59
+
60
+ # return a File from a file included in the main ZIP file
61
+ def read(file)
62
+ @zip_file.file.open(file, 'r') {|f| f.read }
63
+ end
64
+ end
65
+ end; end
@@ -0,0 +1,210 @@
1
+ require 'fileutils'
2
+ require 'rexml/document'
3
+ require 'iconv'
4
+ require 'rubygems'
5
+ require 'zip/zipfilesystem'
6
+ include Zip
7
+
8
+ module Woa; module Energon
9
+ class InvalidOpenXmlDocument < StandardError
10
+ end
11
+
12
+ # This class is an interface with OpenXML files. It manages
13
+ # the content of the file so that caller classes just manage
14
+ # xml Object (ReXml). It's not possible at this time to work
15
+ # with streams beacause of the zip library (rubyzip), it has
16
+ # to be done via files.
17
+ #
18
+ # The support is not fully functional because of rubyzip which
19
+ # is buggy and generates wrong zip file not recognized by Office.
20
+ #
21
+ # :include: rdoc-header
22
+ class OpenXmlHelper
23
+
24
+ TypeWord = 0
25
+ TypeExcel = TypeWord.next
26
+
27
+ attr_reader :type, :shared_strings
28
+ attr_reader :workbook
29
+ attr_reader :worksheets
30
+
31
+
32
+ ############# tempdir ##############
33
+ def OpenXmlHelper.finalize(tmpdir)
34
+ lambda do
35
+ FileUtils.remove_dir(tmpdir, true) if File.exist?(tmpdir)
36
+ end
37
+ end
38
+ ############# tempdir ##############
39
+
40
+
41
+ # * ftemplate: the name of the file
42
+ # * type: the type of the file (TypeWord or TypeExcel)
43
+ def initialize(template, type)
44
+ raise InvalidOpenXmlDocument, "Wrong Type" unless type == TypeWord || type == TypeExcel
45
+ @type = type
46
+ @template = template
47
+
48
+ # @zip_file = ZipFile.open(template)
49
+ ############# tempdir ##############
50
+ n = 0
51
+ begin
52
+ @tmpdir = "tempdir.#{$$}.#{n}.dir"
53
+ n = n.next
54
+ end while File.exist?(@tmpdir)
55
+
56
+ ObjectSpace.define_finalizer(self, OpenXmlHelper.finalize(@tmpdir))
57
+ FileUtils.mkdir(@tmpdir)
58
+
59
+ ZipFile.foreach(template) do |entry|
60
+ next if entry.directory?
61
+ file = "#{@tmpdir}/#{entry.name}"
62
+ dir = File.dirname(file)
63
+ FileUtils.mkdir_p(dir) unless File.exist?(dir)
64
+ File.open(file, 'wb') do |file|
65
+ entry.get_input_stream {|stream| file.write(stream.read) }
66
+ end
67
+ end
68
+ ############# tempdir ##############
69
+
70
+ rels = xml(read("_rels/.rels")) rescue nil
71
+ raise InvalidOpenXmlDocument, "No _rels/.rels file" if rels.nil?
72
+
73
+ elements = REXML::XPath.match(rels, "/Relationships/Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument']")
74
+
75
+ raise InvalidOpenXmlDocument, "This version of OpenXML Helper can't handle OpenXml files with more than one main document" unless elements.size == 1
76
+
77
+ target = elements.first.attribute('Target').to_s
78
+
79
+ @documents = {}
80
+ @shared_strings = nil
81
+ @workbook = nil
82
+ @worksheets = nil
83
+
84
+ case type
85
+ when TypeWord
86
+ add_document(target) if target =~ /^word\/document.xml$/
87
+ when TypeExcel
88
+ if target =~ /^xl\/workbook.xml$/
89
+ workbook = xml(read(target))
90
+ add_document(target, workbook)
91
+ @workbook = workbook
92
+ end
93
+ end
94
+
95
+ raise InvalidOpenXmlDocument if @documents.empty?
96
+
97
+ case type
98
+ when TypeWord
99
+ add_word_documents
100
+ when TypeExcel
101
+ add_excel_documents
102
+ end
103
+ end
104
+
105
+ # create directly a new Excel file
106
+ # * template: the name of the file
107
+ def OpenXmlHelper.new_excel(template)
108
+ OpenXmlHelper.new(template, TypeExcel)
109
+ end
110
+
111
+ # create directly a new Word file
112
+ # * template: the name of the file
113
+ def OpenXmlHelper.new_word(template)
114
+ OpenXmlHelper.new(template, TypeWord)
115
+ end
116
+
117
+ # it returns all the files which could contain some data (Text, Numbers, Dates, ...).
118
+ # All the files who deal with the format, the style, ... are not included
119
+ #
120
+ # Each member of the Array is a REXML::Document
121
+ def documents
122
+ @documents.keys
123
+ end
124
+
125
+ # Save the modified content of the file represented by a REXML::Document
126
+ def save(xml)
127
+ save_xml(xml, @documents[xml]) if @documents.include?(xml)
128
+ end
129
+
130
+ # Write the final document (the zipfile) and close the file
131
+ def write
132
+ save_shared_strings
133
+ # @zip_file.close
134
+ ############# tempdir ##############
135
+ FileUtils.rm(@template) if File.exist?(@template)
136
+ FileUtils.cd(@tmpdir) do |dir|
137
+ system("zip -q -r ../#{@template} .")
138
+ end
139
+ ############# tempdir ##############
140
+ end
141
+
142
+ alias :close :write
143
+
144
+ #########################################
145
+ private
146
+ #########################################
147
+ def save_shared_strings
148
+ save_xml(@shared_strings, @shared_strings_file) unless @shared_strings.nil?
149
+ end
150
+
151
+ def save_xml(xml, file)
152
+ # @zip_file.file.open(file, 'w') do |file|
153
+ ############# tempdir ##############
154
+ File.open("#{@tmpdir}/#{file}", 'wb') do |f|
155
+ ############# tempdir ##############
156
+ # xml.write(f)
157
+ f.write(xml.to_s.unpack('C*').pack('U*'))
158
+ end
159
+ end
160
+
161
+ # add all dependent word files in @documents
162
+ def add_word_documents
163
+ REXML::XPath.match(xml(read('word/_rels/document.xml.rels')), "/Relationships/Relationship").each do |element|
164
+ next unless element.attribute('Type').to_s =~ /\/(footer|header|endnotes|footnotes|glossaryDocument)$/
165
+ add_document("word/#{element.attribute('Target')}")
166
+ end
167
+ end
168
+
169
+ # add all dependent excel files in @documents
170
+ def add_excel_documents
171
+ @worksheets = []
172
+ REXML::XPath.match(xml(read('xl/_rels/workbook.xml.rels')), "/Relationships/Relationship").each do |element|
173
+ type = element.attribute('Type').to_s
174
+ target = "xl/#{element.attribute('Target')}"
175
+ if type =~ /\/(worksheet)$/
176
+ xml = xml(read(target))
177
+ add_document(target, xml)
178
+ @worksheets << xml
179
+ else
180
+ if type =~ /\/(sharedStrings)$/
181
+ raise InvalidOpenXmlDocument, "This version of OpenXML Helper allows only one SharedString file" unless @shared_strings.nil?
182
+ @shared_strings = xml(read(target))
183
+ @shared_strings_file = target
184
+ end
185
+ end
186
+ end
187
+ raise InvalidOpenXmlDocument if @shared_strings.nil?
188
+ end
189
+
190
+ # update @documents from a file
191
+ # it creates the xml
192
+ def add_document(target, xml=nil)
193
+ xml = xml(read(target)) if xml.nil?
194
+ @documents[xml] = target
195
+ end
196
+
197
+ # return the REXML::Document from a file
198
+ def xml(file)
199
+ REXML::Document.new(file)
200
+ end
201
+
202
+ # return a File from a file included in the main ZIP file
203
+ def read(file)
204
+ # @zip_file.file.open(file, 'r') {|f| f.read }
205
+ ############# tempdir ##############
206
+ File.open("#{@tmpdir}/#{file}", 'rb') {|f| f.read}
207
+ ############# tempdir ##############
208
+ end
209
+ end
210
+ end; end