sandrods-odf-report 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest +7 -0
- data/README.textile +150 -0
- data/Rakefile +14 -0
- data/lib/odf-report.rb +174 -0
- data/odf-report.gemspec +34 -0
- data/test/test.odt +0 -0
- data/test/test.rb +36 -0
- metadata +78 -0
data/Manifest
ADDED
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
|
data/odf-report.gemspec
ADDED
@@ -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
|
+
|