sandrods-odf-report 0.1.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.
data/Manifest ADDED
@@ -0,0 +1,7 @@
1
+ lib/odf-report.rb
2
+ odf-report.gemspec
3
+ Rakefile
4
+ README.textile
5
+ test/test.odt
6
+ test/test.rb
7
+ Manifest
data/README.textile ADDED
@@ -0,0 +1,150 @@
1
+ h1. ODF-REPORT
2
+
3
+ Gem for generating .odt files by making strings substitution in a previously created .odt file.
4
+
5
+ <hr/>
6
+
7
+ h3. INSTALL
8
+
9
+ @gem install sandrods-odf-report --source=http://gems.github.com@
10
+
11
+ <hr/>
12
+
13
+ h3. USAGE
14
+
15
+ h4. Step 1 -- the template
16
+
17
+ First of all, you need to create a .odt file to serve as a template
18
+
19
+ Templates are normal .odt files with placeholders for Substitutions
20
+
21
+ There are two kinds of substitutions available: *fields* and *tables*
22
+
23
+ h4. Fields placeholders
24
+
25
+ It's just an upcase sentence, surrounded by brackets. It will be replaced for wathever value you supply.
26
+
27
+ In the folowing example:
28
+
29
+ <pre>
30
+ report = ODFReport.new("Users/john/my_template.odt") do |r|
31
+
32
+ r.add_field :user_name, @user.name
33
+ r.add_field :address, "My new address"
34
+
35
+ end
36
+ </pre>
37
+
38
+ All occurences of @[USER_NAME]@ found in the file will be replaced by the value of @@user.name@ whereas all @[ADDRESS]@ 'es will contains @My new address@
39
+
40
+ It's as simple as that.
41
+
42
+ h4. Table placeholders
43
+
44
+ To use table placeholders, you should create a Table in your document and give it a name. In OpenOffice, it's just a matter of right-clicking the table you just created, choose _Table Properties..._ and type a name in the Name field.
45
+
46
+ If the table has two rows, the first one will be treated as a *header* and left untouched. Otherwise you should use a table with one row only.
47
+
48
+ As with Field placeholders, just insert a @[FIELD_NAME]@ in each cell and let the magic takes place.
49
+
50
+ Taking the folowing example:
51
+
52
+ <pre>
53
+ report = ODFReport.new("Users/john/my_template.odt") do |r|
54
+
55
+ r.add_field "USER_NAME", @user.nome
56
+ r.add_field "ADDRESS", @user.address
57
+
58
+ r.add_table("TABLE_1", @list_of_itens) do |row, item|
59
+ row["ITEM_ID"] = item.id
60
+ row["DESCRIPTION"] = "==> #{item.description}"
61
+ end
62
+
63
+ end
64
+ </pre>
65
+
66
+ and considering you have a table like this in your template
67
+
68
+ <pre>
69
+ ----------------------------
70
+ | [ITEM_ID] | [DESCRIPTON] |
71
+ ----------------------------
72
+ </pre>
73
+
74
+ and a collection @list_of_itens, it will be created one row for each item in the collection, and the replacement will take place accordingly.
75
+
76
+ Any format applied to the fields in the template will be preserved.
77
+
78
+ <hr/><br/>
79
+
80
+ h4. Step 2 -- generating the document
81
+
82
+ It's fairly simple to generate the document. You can use this inside a Rails application or in a standalone script.
83
+
84
+ h4. Generating a document in a Rails application
85
+
86
+ In a controller, you can have a code like this:
87
+
88
+ <pre>
89
+ def print
90
+
91
+ report = ODFReport.new("#{RAILS_ROOT}/app/reports/ticket.odt") do |r|
92
+
93
+ r.add_field(:id, @ticket.id.to_s)
94
+ r.add_field(:created_by, @ticket.created_by)
95
+ r.add_field(:created_at, @ticket.created_at.strftime("%d/%m/%Y - %H:%M"))
96
+ r.add_field(:type, @ticket.type.name)
97
+ r.add_field(:status, @ticket.status_text)
98
+ r.add_field(:date, Time.now.strftime("%d/%m/%Y - %H:%M"))
99
+ r.add_field(:solution, (@ticket.solution || ''))
100
+
101
+ r.add_table("OPERATORS", @ticket.operators) do | row, op |
102
+ row["OPERATOR_NAME"] = "#{op.name} (#{op.department.short_name})"
103
+ end
104
+
105
+ r.add_table("FIELDS", @ticket.fields) do | row, field |
106
+
107
+ if field.is_a?(String)
108
+ row["FIELD_NAME"] = 'Materials'
109
+ row["FIELD_VALUE"] = field
110
+ else
111
+ row["FIELD_NAME"] = field.name
112
+ row["FIELD_VALUE"] = field.text_value || ''
113
+ end
114
+
115
+ end
116
+
117
+ end
118
+
119
+ report_file_name = report.generate
120
+
121
+ send_file(report_file_name)
122
+
123
+ end
124
+ </pre>
125
+
126
+ The @generate@ method will, er... generate the document in a temp dir and returns the full path of the generated file, so you can send it back to the user.
127
+
128
+ _That's all I have to say about that._
129
+
130
+ h4. Generating a document in a standalone script
131
+
132
+ It's just the same as in a Rails app, but you can inform the path where the file will be generated instead of using a temp dir.
133
+
134
+ <pre>
135
+ report = ODFReport.new("ticket.odt") do |r|
136
+
137
+ ... populates the report ...
138
+
139
+ end
140
+
141
+ report.generate("./documents/")
142
+ </pre>
143
+
144
+ <hr/>
145
+
146
+ h3. REQUIREMENTS
147
+
148
+ *rubyzip*: for manipulating the contents of the odt file, since it's actually a zip file.
149
+
150
+
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'echoe'
4
+
5
+ Echoe.new('odf-report', '0.1.1') do |p|
6
+ p.description = "Generates ODF files, given a template (.odt) and data, replacing tags"
7
+ p.url = ""
8
+ p.author = "Sandro Duarte"
9
+ p.email = "sandrods@gmail.com"
10
+ p.ignore_pattern = ["tmp/*", "script/*"]
11
+ p.development_dependencies = []
12
+ p.runtime_dependencies = ['rubyzip >= 0.9.1']
13
+ end
14
+
data/lib/odf-report.rb ADDED
@@ -0,0 +1,174 @@
1
+ require 'rubygems'
2
+ require 'zip/zipfilesystem'
3
+ require 'fileutils'
4
+
5
+ class ODFReport
6
+
7
+ def initialize(template_name, &block)
8
+ @template = template_name
9
+ @data={:values=>{}, :tables=>{}}
10
+
11
+ @tmp_dir = Dir.tmpdir + "/" + random_filename(:prefix=>'odt_')
12
+ Dir.mkdir(@tmp_dir) unless File.exists? @tmp_dir
13
+
14
+ yield self
15
+ end
16
+
17
+ def add_field(field_tag, value)
18
+ @data[:values][field_tag] = value
19
+ end
20
+
21
+ def add_table(table_tag, collection, &block)
22
+
23
+ @data[:tables][table_tag] = []
24
+
25
+ collection.each do |item|
26
+ row = {}
27
+ yield(row, item)
28
+ @data[:tables][table_tag] << row
29
+ end
30
+
31
+ end
32
+
33
+ def generate(dest = nil)
34
+
35
+ if dest
36
+
37
+ FileUtils.cp(@template, dest)
38
+ new_file = dest
39
+
40
+ else
41
+
42
+ FileUtils.cp(@template, @tmp_dir)
43
+ new_file = "#{@tmp_dir}/#{File.basename(@template)}"
44
+
45
+ end
46
+
47
+ %w(content.xml styles.xml).each do |content_file|
48
+
49
+ update_file_from_zip(new_file, content_file) do |txt|
50
+
51
+ replace_fields!(txt)
52
+ replace_tables!(txt)
53
+
54
+ end
55
+
56
+ end
57
+
58
+ new_file
59
+
60
+ end
61
+
62
+ private
63
+
64
+ def update_file_from_zip(zip_file, content_file, &block)
65
+
66
+ Zip::ZipFile.open(zip_file) do |z|
67
+ cont = "#{@tmp_dir}/#{content_file}"
68
+
69
+ z.extract(content_file, cont)
70
+
71
+ txt = ''
72
+
73
+ File.open(cont, "r") do |f|
74
+ txt = f.read
75
+ end
76
+
77
+ yield(txt)
78
+
79
+ File.open(cont, "w") do |f|
80
+ f.write(txt)
81
+ end
82
+
83
+ z.replace(content_file, cont)
84
+ end
85
+
86
+ end
87
+
88
+
89
+ def replace_fields!(content)
90
+ hash_gsub!(content, @data[:values])
91
+ end
92
+
93
+
94
+ def replace_tables!(content)
95
+
96
+ @data[:tables].each do |table_name, records|
97
+
98
+ # search for the table inside the content
99
+ table_rgx = Regexp.new("(<table:table table:name=\"#{table_name}.*?>.*?<\/table:table>)", "m")
100
+ table_match = content.match(table_rgx)
101
+
102
+ if table_match
103
+ table = table_match[0]
104
+
105
+ # extract the table from the content
106
+ content.gsub!(table, "[TABLE_#{table_name}]")
107
+
108
+ # search for the table:row's
109
+ row_rgx = Regexp.new("(<table:table-row.*?<\/table:table-row>)", "m")
110
+
111
+ # use scan (instead of match) as the table can have more than one table-row (header and data)
112
+ # and scan returns all matches
113
+ row_match = table.scan(row_rgx)
114
+
115
+ unless row_match.empty?
116
+
117
+ # If there more than one line in the table, takes the second entry (row_match[1])
118
+ # since the first one represents the column header.
119
+ # If there just one line, takes the first line. Besides, since the entry is an Array itself,
120
+ # takes the entry's first element ( entry[0] )
121
+ model_row = (row_match[1] || row_match[0])[0]
122
+
123
+ # extract the row from the table
124
+ table.gsub!(model_row, "[ROW_#{table_name}]")
125
+
126
+ new_rows = ""
127
+
128
+ # for each record
129
+ records.each do |_values|
130
+
131
+ # generates one new row (table-row), based in the model extracted
132
+ # from the original table
133
+ tmp_row = model_row.dup
134
+
135
+ # replace values in the model_row and stores in new_rows
136
+ hash_gsub!(tmp_row, _values)
137
+
138
+ new_rows << tmp_row
139
+ end
140
+
141
+ # replace back the lines into the table
142
+ table.gsub!("[ROW_#{table_name}]", new_rows)
143
+
144
+ end # unless row_match.empty?
145
+
146
+ # replace back the table into content
147
+ content.gsub!("[TABLE_#{table_name}]", table)
148
+
149
+ end # if table match
150
+
151
+ end # tables each
152
+
153
+ end # replace_tables
154
+
155
+ def hash_gsub!(_text, hash_of_values)
156
+ hash_of_values.each do |key, val|
157
+ _text.gsub!("[#{key.to_s.upcase}]", val.to_s) unless val.nil?
158
+ end
159
+ end
160
+
161
+ def random_filename(opts={})
162
+ opts = {:chars => ('0'..'9').to_a + ('A'..'F').to_a + ('a'..'f').to_a,
163
+ :length => 24, :prefix => '', :suffix => '',
164
+ :verify => true, :attempts => 10}.merge(opts)
165
+ opts[:attempts].times do
166
+ filename = ''
167
+ opts[:length].times { filename << opts[:chars][rand(opts[:chars].size)] }
168
+ filename = opts[:prefix] + filename + opts[:suffix]
169
+ return filename unless opts[:verify] && File.exists?(filename)
170
+ end
171
+ nil
172
+ end
173
+
174
+ end
@@ -0,0 +1,34 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{odf-report}
5
+ s.version = "0.1.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Sandro Duarte"]
9
+ s.date = %q{2009-07-28}
10
+ s.description = %q{Generates ODF files, given a template (.odt) and data, replacing tags}
11
+ s.email = %q{sandrods@gmail.com}
12
+ s.extra_rdoc_files = ["lib/odf-report.rb", "README.textile"]
13
+ s.files = ["lib/odf-report.rb", "odf-report.gemspec", "Rakefile", "README.textile", "test/test.odt", "test/test.rb", "Manifest"]
14
+ s.has_rdoc = true
15
+ s.homepage = %q{}
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Odf-report", "--main", "README.textile"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{odf-report}
19
+ s.rubygems_version = %q{1.3.1}
20
+ s.summary = %q{Generates ODF files, given a template (.odt) and data, replacing tags}
21
+
22
+ if s.respond_to? :specification_version then
23
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
24
+ s.specification_version = 2
25
+
26
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
+ s.add_runtime_dependency(%q<rubyzip>, [">= 0", "= 0.9.1"])
28
+ else
29
+ s.add_dependency(%q<rubyzip>, [">= 0", "= 0.9.1"])
30
+ end
31
+ else
32
+ s.add_dependency(%q<rubyzip>, [">= 0", "= 0.9.1"])
33
+ end
34
+ end
data/test/test.odt ADDED
Binary file
data/test/test.rb ADDED
@@ -0,0 +1,36 @@
1
+ require '../lib/odf-report'
2
+
3
+ col1 = []
4
+ col1 << {:nome=>"Campo zero um", :id=>"Campo dois", :ender=>"tres tres"}
5
+ col1 << {:nome=>"zero um", :id=>"dois", :ender=>"tres"}
6
+ col1 << {:nome=>"Campo um", :id=>"Campo erthe dois", :ender=>"tres tres tres"}
7
+
8
+ col2 = []
9
+ col2 << {:nome=>"Campo zero um", :id=>"Campo dois", :ender=>"tres tres", :fone=>99025668, :cep=>"90420-002"}
10
+ col2 << {:nome=>"sandro", :id=>"45", :ender=>"minha casa", :fone=>88774451, :cep=>"90490-002"}
11
+ col2 << {:nome=>"ellen bicca", :id=>"77", :ender=>"casa dela", :fone=>77025668, :cep=>"94420-002"}
12
+
13
+ report = ODFReport.new("test.odt") do |r|
14
+
15
+ r.add_field("CAMPO_CAB", "Este campo estava no cabecalho")
16
+
17
+ r.add_field("TAG_01", "Nova tag")
18
+ r.add_field("TAG_02", "TAG-2 -> Nova tag")
19
+
20
+ r.add_table("TABELA_01", col1) do |row, item|
21
+ row["CAMPO_01"] = item[:id]
22
+ row["CAMPO_02"] = item[:nome]
23
+ row["CAMPO_03"] = item[:ender]
24
+ end
25
+
26
+ r.add_table("TABELA_02", col2) do |row, item|
27
+ row["CAMPO_04"] = item[:id]
28
+ row["CAMPO_05"] = item[:nome]
29
+ row["CAMPO_06"] = item[:ender]
30
+ row["CAMPO_07"] = item[:fone]
31
+ row["CAMPO_08"] = item[:cep]
32
+ end
33
+
34
+ end
35
+
36
+ report.generate("result.odt")
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sandrods-odf-report
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Sandro Duarte
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-28 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rubyzip
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ - - "="
25
+ - !ruby/object:Gem::Version
26
+ version: 0.9.1
27
+ version:
28
+ description: Generates ODF files, given a template (.odt) and data, replacing tags
29
+ email: sandrods@gmail.com
30
+ executables: []
31
+
32
+ extensions: []
33
+
34
+ extra_rdoc_files:
35
+ - lib/odf-report.rb
36
+ - README.textile
37
+ files:
38
+ - lib/odf-report.rb
39
+ - odf-report.gemspec
40
+ - Rakefile
41
+ - README.textile
42
+ - test/test.odt
43
+ - test/test.rb
44
+ - Manifest
45
+ has_rdoc: true
46
+ homepage: ""
47
+ licenses:
48
+ post_install_message:
49
+ rdoc_options:
50
+ - --line-numbers
51
+ - --inline-source
52
+ - --title
53
+ - Odf-report
54
+ - --main
55
+ - README.textile
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "1.2"
69
+ version:
70
+ requirements: []
71
+
72
+ rubyforge_project: odf-report
73
+ rubygems_version: 1.3.5
74
+ signing_key:
75
+ specification_version: 2
76
+ summary: Generates ODF files, given a template (.odt) and data, replacing tags
77
+ test_files: []
78
+