energon 0.0.1

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.
@@ -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