energon 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,262 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+
4
+ module Woa; module Energon
5
+
6
+ class NoDataFound < StandardError #:nodoc:
7
+ end
8
+ class WrongData < StandardError #:nodoc:
9
+ end
10
+
11
+ # This class goal is to parse the placeholder, to make the correspondance with the right data
12
+ # and then to format the data according to the syntax extracted from the placeholder.
13
+ #
14
+ # Each placeholder is composed of a placeholder and a bunch of format functions.
15
+ #
16
+ # This class cannot be instanced. There is only one Class method: merge.
17
+ #
18
+ # == Placeholder
19
+ #
20
+ # The placeholder is composed by several names separated by a period (like Ruby Object syntax). (<tt>customer.name</tt>)
21
+ # For now only two members are permitted.
22
+ #
23
+ # Placeholders with only one name are static (<tt>author</tt>). They can only be replaced by a single data (<tt>"Jack Bauer"</tt>).
24
+ #
25
+ # Placeholders with two names are collections (+customer.name+). They are replaced by a bunch of data
26
+ # (<tt>["J. McGerol", "P. Fishno", "L. Rankka"]</tt>). If the second member is a plural, it means that the collection must be
27
+ # joined in a row and replace the placeholder (<tt>"J. McGerol, P. Fishno, L. Ranka"</tt>). The way that collections are imported
28
+ # depends on the type of exports used (see Document for more details).
29
+ #
30
+ # == Format Function
31
+ #
32
+ # Each placeholder can be added a set of functions separeted by a period. All format functions use the same syntax.
33
+ # A +f+ followed by a single caracter (parenthesa by default +(+ but could be anything). It takes then the syntax and
34
+ # finally the closed caracter (closed parenthesa by default +)+ but the same previously defined if not default).
35
+ # In all cases, the delimitors are echaped in the syntax.
36
+ #
37
+ # author.f(syntax1).f(syntax2)
38
+ # f(syntax \(here\)) => "syntax (here)"
39
+ # f/another syntax with another delimiter(\/)/ => "another syntax with another delimiter (/)"
40
+ #
41
+ # == Syntax
42
+ # Any kind of data can be used. By default, all data are interpreted as a String. Except the following types:
43
+ # * Numeric and its derivates
44
+ # * Date, DateTime and Time
45
+ # * Boolean
46
+ # * Array
47
+ # * Picture
48
+ #
49
+ # A syntax is possible for each type:
50
+ # ===String
51
+ #
52
+ # 0..N, end='' to limit the size of the string to N caracters.
53
+ # end is optionnal, its value by default is an empty string.
54
+ # If end is not empty and the original string has been truncated,
55
+ # end is added at the end of the new string.
56
+ # "Hello world".f(0..5, "...") => "Hello..."
57
+ #
58
+ # See String[http:www.ruby-doc.org/core/classes/String.html] class help for more details.
59
+ # * fixnum
60
+ # + str or << str or concat(str)
61
+ # [fixnum] or slice(fixnum)
62
+ # [fixnum, fixnum] or slice(fixnum, fixnum)
63
+ # [range] or slice(range)
64
+ # [regexp] or slice(regexp)
65
+ # [regexp, regexp] or slice(regexp, regexp)
66
+ # [str] or slice(str)
67
+ # capitalize
68
+ # center(integer, padstr)
69
+ # chomp(separator=$/)
70
+ # chop
71
+ # count([str]+)
72
+ # crypt(other_str)
73
+ # delete([str]+)
74
+ # downcase
75
+ # dump
76
+ # empty?
77
+ # equal(str) or == str or <==> str
78
+ # gsub(pattern, replacement)
79
+ # hash
80
+ # include(str)
81
+ # insert(index, str)
82
+ # inspect
83
+ # length
84
+ # ljust(integer, padstr='')
85
+ # lstrip
86
+ # succ or .next
87
+ # reverse
88
+ # rjust
89
+ # rstrip
90
+ # squeeze
91
+ # strip
92
+ # sub(pattern, replacement)
93
+ # swapcase
94
+ # tr(from_str, to_str)
95
+ # upcase
96
+ #
97
+ # ===Numeric
98
+ # str : str is format string using the sprintf syntax.
99
+ #
100
+ # See the following classes for more details:
101
+ # Numeric[http:www.ruby-doc.org/core/classes/Numeric.html],
102
+ # Integer[http:www.ruby-doc.org/core/classes/Integer.html],
103
+ # Fixnum[http:www.ruby-doc.org/core/classes/Fixnum.html],
104
+ # Bignum[http:www.ruby-doc.org/core/classes/Bignum.html] and
105
+ # Float[http:www.ruby-doc.org/core/classes/Float.html]
106
+ #
107
+ # + numeric
108
+ # - numeric
109
+ # * numeric
110
+ # ** numeric or power(numeric)
111
+ # << numeric
112
+ # >> numeric
113
+ # ^ numeric
114
+ # / numeric or .div(numeric)
115
+ # | numeric
116
+ # ~ numeric
117
+ # ceil
118
+ # div(numeric)
119
+ # eql?(numeric)
120
+ # floor
121
+ # gcd
122
+ # integer?
123
+ # lcm
124
+ # modulo(numeric) or % numeric
125
+ # next or succ
126
+ # remainder(numeric)
127
+ # round
128
+ # size
129
+ # truncate
130
+ # to_i (just for Floats)
131
+ # zero?
132
+ #
133
+ # ===DateTime
134
+ # See the following classes for more details:
135
+ # Time[http:www.ruby-doc.org/core/classes/Time.html],
136
+ # Date[http:www.ruby-doc.org/core/classes/Date.html] and
137
+ # DateTime[http:www.ruby-doc.org/core/classes/DateTime.html]
138
+ #
139
+ # + numeric
140
+ # - numeric
141
+ # << numeric
142
+ # << numeric
143
+ # hour
144
+ # min
145
+ # sec
146
+ # zone
147
+ # strftime
148
+ # httpdate
149
+ # asctime or ctime
150
+ # day or mday
151
+ # eql?(date)
152
+ # getlocal
153
+ # getgmt or getutc
154
+ # hour
155
+ # iso8601 or xmlschema
156
+ # localtime
157
+ # day
158
+ # min
159
+ # month
160
+ # rfc822 or rfc2822
161
+ # sec
162
+ # strftime(format) or format strftime("%d/%m/%y") <==> "%d/%m/%y"
163
+ # succ
164
+ # usec or tv_usec
165
+ # wday
166
+ # yday
167
+ # year
168
+ # zone
169
+ #
170
+ # ===Boolean
171
+ # ! : invert the boolean
172
+ #
173
+ # str_true, str_false : set the output for true and for false
174
+ # true.f("vrai", "faux") => "vrai"
175
+ # false.f/"vrai", "faux"/ => "vrai"
176
+ #
177
+ # ===Array
178
+ # delimitor, last_delimitor : set the delimitor for joining each elements of the Array and the last delimitor
179
+ # [1, 2, 3].f(", ", " and ") => "1, 2 and 3"
180
+ #
181
+ # ===Picture
182
+ # Not done yet!
183
+ #
184
+ # == Example
185
+ #
186
+ # Parser.merge("author.f(0..6).f(capitalize)", {:author => "JoHn Doe", :t1 => 1, :t2 => 2, :t3 => 3}) #"John"
187
+ #
188
+ # :include: rdoc-header
189
+ class Parser
190
+ def initialize #:nodoc:
191
+ raise Exception.new("Cannot create a new instance of this class")
192
+ end
193
+
194
+
195
+ # Find the corresponding data in datas and format it according to the placeholder name
196
+ # and placeholder format syntax. The final value is returned.
197
+ # 1. <tt>placeholder</tt>: a String representing a placeholder (xxx)
198
+ # 2. <tt>datas</tt> : a Hash of all the datas
199
+ def Parser.merge(placeholder, datas)
200
+ static, dynamic, format = Parser.extract_placeholder(placeholder)
201
+ specific = Parser.get_data(static, dynamic, datas)
202
+ # Apply format here
203
+ end
204
+
205
+ #########
206
+ private #
207
+ #########
208
+
209
+ def Parser.extract_placeholder(placeholder)
210
+ placeholder.scan(/^(\w+)(\.\w+)?(\.f.*)*$/) do |static, dynamic, format|
211
+ dynamic = dynamic.nil? ? nil : dynamic.to_s.gsub(/^\./, '')
212
+ format = format.nil? ? nil : format.to_s.gsub(/^\./, '')
213
+ return [static, dynamic, format]
214
+ end
215
+ end
216
+
217
+ def Parser.get_data(static, dynamic, datas)
218
+ raise NoPlaceholderFound if static.nil?
219
+ error = "placeholder '#{static + (dynamic.nil? ? '' : '.' + dynamic)}'"
220
+
221
+ specific_data = datas[static] rescue nil
222
+ if specific_data.nil?
223
+ specific_data = datas[static.to_sym] rescue nil
224
+ raise NoDataFound, error if specific_data.nil?
225
+ end
226
+
227
+ # static
228
+ raise WrongData if dynamic.nil? && specific_data.is_a?(Array)
229
+ return specific_data if dynamic.nil?
230
+
231
+ # dynamic
232
+ raise NoDataFound, error unless specific_data.is_a?(Array)
233
+ raise NoDataFound, error if specific_data.empty?
234
+
235
+ singular = Inflector::singularize(dynamic)
236
+ singular = nil if singular == dynamic
237
+ elements = []
238
+
239
+ value = dynamic
240
+ specific_data.each do |element|
241
+ method = element.method(value.to_sym) rescue nil
242
+ unless method.nil? || element.is_a?(Hash) # method
243
+ elements << method.call
244
+ else # array
245
+ elt = element[value.to_sym] rescue nil
246
+ elt = element[value] if elt.nil? rescue nil
247
+ if elt.nil?
248
+ raise NoDataFound, error if singular.nil? || value == singular
249
+ value = singular
250
+ retry
251
+ end
252
+ elements << elt
253
+ end
254
+ end
255
+ ret = elements
256
+ if value == singular
257
+ ret = elements.to_sentence(:skip_last_comma => true)
258
+ end
259
+ ret
260
+ end
261
+ end
262
+ end; end
@@ -0,0 +1,94 @@
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 Word 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 WordDocument < Document
15
+
16
+ def write()
17
+ raise NoPlaceholderFound if @placeholders.empty?
18
+ rows = {}
19
+ @placeholders.each do |placeholder|
20
+ data = Parser.merge(placeholder[:placeholder], @datas)
21
+ element = placeholder[:element]
22
+ delimiter = placeholder[:delimiter]
23
+ if data.is_a?(Array)
24
+ row = XPath.first(element, 'ancestor::w:tr')
25
+ if row.nil?
26
+ element.text = element.text.to_s.gsub("#{delimiter}#{placeholder[:placeholder]}#{delimiter}", data.shift.to_s)
27
+ while !data.empty?
28
+ t = Element.new('w:t')
29
+ t.text = data.shift.to_s
30
+ element.parent.insert_after(element, t)
31
+ element.parent.insert_after(element, Element.new('w:br'))
32
+ element = t
33
+ end
34
+ else
35
+ rows[row] = [] if rows[row].nil?
36
+ rows[row] << {:element => element, :data => data}
37
+ end
38
+ else
39
+ element.text = element.text.to_s.gsub("#{delimiter}#{placeholder[:placeholder]}#{delimiter}", data.to_s)
40
+ end
41
+ end
42
+ rows.each do |row, cells|
43
+ continue = true
44
+ while continue
45
+ cells.each do |cell|
46
+ size = cell[:data].size
47
+ data = cell[:data].pop
48
+ element = cell[:element]
49
+ if data.nil?
50
+ continue = false
51
+ break
52
+ end
53
+ element.text = data
54
+ end
55
+ row.parent.insert_after(row, row.deep_clone) if continue
56
+ end
57
+ row.parent.delete(row)
58
+ end
59
+ @documents.each {|document| @openxml.save(document) }
60
+ @openxml.write
61
+ end
62
+ alias :close :write
63
+
64
+ #############################
65
+ private
66
+ #############################
67
+ def extract_placeholders
68
+ @openxml = OpenXmlHelper.new_word(@template)
69
+ @documents = @openxml.documents
70
+ @documents.each do |document|
71
+ XPath.each(document, '//w:t') do |element|
72
+ element.text.to_s.scan(Regexp.new("#{@delimiter}((#{Regexp.escape('\\' + @delimiter)}|[^#{@delimiter}])*)#{@delimiter}")) do |match, non_used|
73
+ @placeholders << {:placeholder => match, :element => element, :delimiter => @delimiter}
74
+ end
75
+ end
76
+ XPath.each(document, '//w:t[text()]') do |element|
77
+ next unless Regexp.new("^(#{Regexp.escape('\\' + @delimiter)}|[^#{@delimiter}])*$") =~ element.text.to_s
78
+ placeholder = Regexp.last_match(0)
79
+
80
+ before = XPath.first(element, '../preceding-sibling::w:r/w:t[text()="' + @delimiter + '"]')
81
+ next if before.nil?
82
+
83
+ after = XPath.first(element, '../following-sibling::w:r')
84
+ next if after.nil?
85
+ after = XPath.first(after, 'w:t[text()="' + @delimiter + '"]')
86
+ next if after.nil?
87
+ before.parent.delete(before)
88
+ after.parent.delete(after)
89
+ @placeholders << {:placeholder => placeholder, :element => element, :delimiter => ''}
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end; end
data/lib/energon.rb ADDED
@@ -0,0 +1,24 @@
1
+ require 'energon/document'
2
+ require 'energon/word_document'
3
+ require 'energon/excel_document'
4
+ require 'energon/open_xml_helper'
5
+ require 'energon/open_document_helper'
6
+ require 'energon/od_document'
7
+
8
+ require 'energon/parser'
9
+
10
+
11
+ # The Woa module contains all modules made by Woa! Kft.
12
+ #
13
+ # You can reach Woa! by visiting http://www.woa.hu or by mailing us to mailto:contact@woa.hu
14
+ #
15
+ # :include: rdoc-header
16
+ module Woa
17
+ # The Energon module contains all modules and classes used for the Energon projet (http://www.woa.hu).
18
+ #
19
+ # See README[link:files/README.html] for more details on Energon.
20
+ #
21
+ # :include: rdoc-header
22
+ module Energon
23
+ end
24
+ end
data/rakefile.rb ADDED
@@ -0,0 +1,59 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rake/gempackagetask'
5
+
6
+ task :default => :test
7
+
8
+ desc "Run the tests"
9
+ Rake::TestTask::new do |t|
10
+ t.test_files = FileList['test/test_*.rb']
11
+ t.verbose = true
12
+ end
13
+
14
+ desc "Generate the documentation"
15
+ Rake::RDocTask::new do |rdoc|
16
+ rdoc.rdoc_dir = 'energon-doc/'
17
+ rdoc.title = "Energon Documentation"
18
+ rdoc.options << '--line-numbers' << '--inline-source'
19
+ rdoc.rdoc_files.include('README')
20
+ rdoc.rdoc_files.include('MIT-LICENSE')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
23
+
24
+ spec = Gem::Specification::new do |s|
25
+ s.platform = Gem::Platform::RUBY
26
+
27
+ s.name = 'energon'
28
+ s.version = "0.0.1"
29
+ s.summary = "Report engine outputting Word/Excel OpenXML documents"
30
+ s.description = <<EOF
31
+ Energon is a report engine written in ruby. It allows you to
32
+ create complex and reliable reports from your templates.
33
+ Both templates and output documents are based on OpenXML format.
34
+ Energon supports currently Text, Word and Excel documents.
35
+ EOF
36
+ s.author = 'Woa! Kft'
37
+ s.email = 'energon@woa.hu'
38
+ s.homepage = "http://www.woa.hu"
39
+
40
+ s.requirements << 'rubyzip, 0.9.1 or greater'
41
+ s.add_dependency('rubyzip', '>= 0.9.1')
42
+
43
+ s.requirements << 'activesupport, 1.3.1 or greater'
44
+ s.add_dependency('active_support', '>= 1.3.1')
45
+
46
+ s.require_path = 'lib'
47
+ s.files = FileList["lib/**/*.rb", "test/**/*.rb", "README","MIT-LICENSE","rakefile.rb"]
48
+ s.test_files = FileList['test/test*.rb']
49
+
50
+ s.has_rdoc = true
51
+ s.extra_rdoc_files = ["README"]
52
+ s.rdoc_options.concat ['--main', 'README']
53
+ end
54
+
55
+ desc "Package the library as a gem"
56
+ Rake::GemPackageTask.new(spec) do |pkg|
57
+ pkg.need_zip = true
58
+ pkg.need_tar = true
59
+ end
@@ -0,0 +1,150 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'energon'
4
+ require 'test/unit'
5
+ require 'ostruct'
6
+
7
+ include Woa::Energon
8
+
9
+ class TestDocument < Test::Unit::TestCase
10
+
11
+ # simple output with only one static placeholder
12
+ def test_static
13
+ document = Document.new("@test1@ bla")
14
+ document.add_value(:test1, "try it")
15
+ assert_equal("try it bla", document.write)
16
+ end
17
+
18
+ # simple output with two identic static placeholders
19
+ def test_static_parameter_twice
20
+ document = Document.new("@test1@ -- @test1@")
21
+ document.add_value(:test1, "try")
22
+ assert_equal("try -- try", document.write)
23
+ end
24
+
25
+ # multilines output with two identic static placeholders
26
+ def test_static_parameter_twice_multiline
27
+ document = Document.new("@test1@ -\n- @test1@")
28
+ document.add_value(:test1, "try")
29
+ assert_equal("try -\n- try", document.write)
30
+ end
31
+
32
+ # multilines output with only static placeholders
33
+ def test_static_parameters_multiline
34
+ document = Document.new("@test1@ bla \n @test2@ @test3@")
35
+ document.add_value(:test1, "try it")
36
+ document.add_values(:test2 => "test_2",
37
+ :test3 => 'test_3')
38
+ assert_equal("try it bla \n test_2 test_3", document.write)
39
+ end
40
+
41
+ # output without the data corresponding to the static placeholder
42
+ def test_static_missing
43
+ document = Document.new("@test1@ bla")
44
+ document.add_value(:non_existant_test, "try it")
45
+ assert_raise(NoDataFound) {document.write}
46
+ end
47
+
48
+ # simple output with a dynamic output in a Hash
49
+ def test_dynamic
50
+ document = Document.new("@test1.name@ bla")
51
+ document.add_value(:test1, [{:name => 'foo'},
52
+ {:name => 'bar'},
53
+ {:name => 'baz'}])
54
+ assert_equal("foo\nbar\nbaz bla", document.write)
55
+ end
56
+
57
+ # output with a missing dynamic field in one of the data in a Hash
58
+ def test_dynamic_missing_placeholder
59
+ document = Document.new("@test1.name@ bla")
60
+ document.add_value(:test1, [{:name => 'foo'},
61
+ {:name => 'bar'},
62
+ {:no_name => 'baz'}])
63
+ assert_raise(NoDataFound) {document.write}
64
+ end
65
+
66
+ # output with no data
67
+ def test_dynamic_no_data
68
+ document = Document.new("@test1.name@ bla")
69
+ document.add_value(:test1, [])
70
+ assert_raise(NoDataFound) {document.write}
71
+ end
72
+
73
+ # multiline output with dynamic placeholder and data in an Object
74
+ def test_dynamic_multiline_data
75
+ datas, names, logins = get_data
76
+ document = Document.new("@test1.name@\n====\n@test1.login@")
77
+ document.add_value(:test1, datas)
78
+ assert_equal("#{names}====#{logins}", document.write)
79
+ end
80
+
81
+ # multiline output with a missing data from an Object
82
+ def test_dynamic_multiline_missing_data
83
+ datas, names, logins = get_data
84
+ document = Document.new("@test1.name@\n====\n@test1.birthdate@")
85
+ document.add_value(:test1, datas)
86
+ assert_raise(NoDataFound) {document.write}
87
+ end
88
+
89
+ # delimiter different from the default one
90
+ def test_delimiter
91
+ datas, names, logins = get_data
92
+ document = Document.new("&test1.name&\n====\n&test1.login&", "&")
93
+ document.add_value(:test1, datas)
94
+ assert_equal("#{names}====#{logins}", document.write)
95
+ end
96
+
97
+ def test_invalid_delimiters
98
+ assert_raise EnergonError do
99
+ Document.new("&test1.name&\n====\n&test1.login&", "")
100
+ end
101
+ end
102
+
103
+ # multiline output with dynamic placeholder and data in an Object
104
+ def test_valid
105
+ datas, names, logins = get_data
106
+
107
+ document = Document.new("@test1.name@\n====\n@test1.login@")
108
+ assert(!document.valid?)
109
+ document.add_value(:test1, datas)
110
+ assert(document.valid?)
111
+ end
112
+
113
+ def test_no_placeholder
114
+ document = Document.new('foo bar ruby')
115
+ document.add_value(:test1, "Hello")
116
+ assert_raise(NoPlaceholderFound) { document.write }
117
+ end
118
+
119
+ def test_plural
120
+ datas = []
121
+ datas << {:name => 'foo'}
122
+ datas << {:name => 'bar'}
123
+ datas << {:name => 'ruby'}
124
+ datas << {:name => 'woa'}
125
+
126
+ document = Document.new('@user.names@')
127
+ document.add_value(:user, datas)
128
+ assert_equal('foo, bar, ruby and woa', document.write)
129
+ end
130
+
131
+ ###########################################################################
132
+ private
133
+ ###########################################################################
134
+
135
+ # TODO: replace with yml file
136
+ def get_data
137
+ datas = []
138
+ names = ""
139
+ logins = ""
140
+ 5.times do |i|
141
+ element = OpenStruct.new
142
+ element.name = "name#{i}"
143
+ element.login = "login#{i}"
144
+ datas << element
145
+ names << "name#{i}\n"
146
+ logins << "\nlogin#{i}"
147
+ end
148
+ return datas, names, logins
149
+ end
150
+ end
@@ -0,0 +1,36 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'energon'
4
+ require 'test/unit'
5
+ require 'fileutils'
6
+
7
+ include Woa::Energon
8
+
9
+ class TestExcelDocument < Test::Unit::TestCase
10
+ def test_1
11
+ FileUtils.cp('test/openxml/excel_template.xlsx', 'excel.xlsx')
12
+ excel = ExcelDocument.new('excel.xlsx', "%")
13
+ excel.add_value(:date, Time.now)
14
+ excel.add_value(:title, "Woa Title!")
15
+ excel.add_value(:footer, "Woa Footer!")
16
+ excel.add_value(:author, "Woa! Kft")
17
+ excel.add_value(:revision, 1.42)
18
+
19
+ elements = []
20
+ 5.times do |i|
21
+ element = OpenStruct.new
22
+ element.date = Time.now + (rand * 10000000).to_i
23
+ element.login = "woa (#{i})"
24
+ element.name = "Woa! Kft (#{i})"
25
+ element.password = "******** (#{i})"
26
+ element.birthdate = Time.new + (rand * 10000000).to_i
27
+ element.address = "Kennedy st. (#{i})"
28
+ element.city = "Dallas (#{i})"
29
+ elements << element
30
+ end
31
+
32
+ excel.add_value('user', elements)
33
+
34
+ excel.close
35
+ end
36
+ end
@@ -0,0 +1,49 @@
1
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
2
+
3
+ require 'energon'
4
+ require 'test/unit'
5
+ require 'fileutils'
6
+ require 'rexml/document'
7
+
8
+ include Woa::Energon
9
+
10
+ class TestOdDocument < Test::Unit::TestCase
11
+ def test_word_and_excel
12
+ [{:ext => 'ods', :name => 'excel'}, {:ext => 'odt', :name => 'word'}].each do |hash|
13
+ orig = "test/opendocument/template.#{hash[:ext]}"
14
+ file = "#{hash[:name]}.#{hash[:ext]}"
15
+
16
+ FileUtils.cp(orig, file)
17
+ ods = OdDocument.new(file)
18
+ ods.add_value(:title, "Woa! Title")
19
+ ods.add_value(:date, "22/01/2007")
20
+
21
+ users = []
22
+ customers = []
23
+ 5.times do |i|
24
+ user = OpenStruct.new
25
+ user.login = "login (#{i})"
26
+ user.firstname = "firstname (#{i})"
27
+ user.lastname = "lastname (#{i})"
28
+ users << user
29
+
30
+ customer = OpenStruct.new
31
+ customer.name = "name(#{i})"
32
+ customers << customer
33
+ end
34
+
35
+ ods.add_value(:customer, customers)
36
+ ods.add_value(:user, users)
37
+ ods.write
38
+
39
+ content_orig = content_new = nil
40
+
41
+ ZipFile.open("test/opendocument/template_test.#{hash[:ext]}") {|zipfile| content_orig = zipfile.file.read("content.xml")}
42
+ ZipFile.open(file) {|zipfile| content_new = zipfile.file.read("content.xml")}
43
+
44
+ assert_equal(content_orig, content_new)
45
+
46
+ FileUtils.rm(file)
47
+ end
48
+ end
49
+ end