odf-report 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 639774f385b52ff5939a37b9e5d46f0ef32883fa
4
- data.tar.gz: 518447cce1c1ccae65960ffd0d75b617f3bb48bf
2
+ SHA256:
3
+ metadata.gz: 561d0b3f786d5514b046221b73e9d9056b8ea4b41e74c91bb6ddfd41ce6919fc
4
+ data.tar.gz: 062e1f340e3923cb3f5e977844a2bc9acc8de1d44496cd3a9dae19d08de92c83
5
5
  SHA512:
6
- metadata.gz: 823da1a19f5c7fa4d44d2f130fd208658fe38472ea252c71427e2a5580126f882c40ba5a3ccb0d4bcc55e93e54d739fd22142fc3cfb540696fa0d52539227fdd
7
- data.tar.gz: ba034ba239cd822fccb1c08e2586d33aa3c7ffd9268dc372fbac54cdd5b7f2b09f63d7aa69e48b83bcc1056cd64d4e4ced757c226771eaf999428f5144f33518
6
+ metadata.gz: c15b086a652768a8199d45ba92ad67f3d2f1d920757d2605b3903f0b41c2f05af8a23be7234c4ef5805f32f368e8a7c44180d133584216f65f49f8e6d50c0538
7
+ data.tar.gz: 3b9b7907b90d3316dc63afa19e52df2c0d0b6e5e085f3454077cf78bd30916458b582093d403d998450123300e067a7ebf19bc69833bd6d0ddf2aff608b008f6
@@ -0,0 +1,218 @@
1
+
2
+ # ODF-REPORT
3
+
4
+ Gem for generating .odt files by making strings, images, tables and sections replacements in a previously created .odt file.
5
+
6
+ ### NEW
7
+
8
+ * you can provide an `io:` param to the report, so you can use templates stored in the database.
9
+ * now uses rubyzip ~> **1.2.0**
10
+
11
+ ## INSTALL
12
+
13
+ In your Gemfile
14
+ ```ruby
15
+ gem 'odf-report'
16
+ ```
17
+
18
+ ## USAGE
19
+
20
+ ### Step 1 -- the template
21
+
22
+ First of all, you need a `.odt` file to serve as a template.
23
+ Templates are normal .odt files with `[PLACEHOLDERS]` for *substitutions*.
24
+ There are *four* kinds of substitutions available:
25
+ * fields
26
+ * tables
27
+ * images
28
+ * sections
29
+
30
+ #### Fields
31
+
32
+ It's just an upcase sentence, surrounded by brackets. It will be replaced for wathever value you supply.
33
+
34
+ In the folowing example:
35
+
36
+ ```ruby
37
+ report = ODFReport::Report.new("Users/john/my_template.odt") do |r|
38
+ r.add_field :user_name, @user.name
39
+ r.add_field :address, "My new address"
40
+ end
41
+ ```
42
+
43
+ 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`
44
+
45
+
46
+ #### Tables
47
+
48
+ 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.
49
+
50
+ If you inform `header: true`, the first row will be treated as a *header* and left untouched. The remaining rows will be used as the template for the table.
51
+
52
+ If you have more than one template row, they will be cycled. This is usefull for making zebra tables.
53
+
54
+ As with **Field placeholders**, just insert a `[FIELD_NAME]` in each cell and let the magic takes place.
55
+
56
+ Taking the folowing example:
57
+
58
+ ```ruby
59
+ report = ODFReport::Report.new("Users/john/my_template.odt") do |r|
60
+
61
+ r.add_field "USER_NAME", @user.nome
62
+ r.add_field "ADDRESS", @user.address
63
+
64
+ r.add_table("TABLE_1", @list_of_itens, :header=>true) do |t|
65
+ t.add_column(:item_id, :id)
66
+ t.add_column(:description) { |item| "==> #{item.description}" }
67
+ end
68
+
69
+ end
70
+ ```
71
+
72
+ and considering you have a table like this in your template
73
+
74
+ | #ID | Description |
75
+ |--|--|
76
+ | [ITEM_ID] | [DESCRIPTION] |
77
+
78
+
79
+ and a collection `@list_of_itens`, it will create one row for each item in the collection, and the replacement will take place accordingly.
80
+
81
+ Any format applied to the fields in the template will be preserved.
82
+
83
+
84
+ ### Images
85
+
86
+ You must put a mock image in your `.odt` template and give it a name. That name will be used to replace the mock image for the actual image.
87
+ You can also assign any properties you want to the mock image and they will be kept once the image is replaced.
88
+
89
+ An image replace would look like this:
90
+
91
+ ```ruby
92
+ report = ODFReport::Report.new("my_template.odt") do |r|
93
+ r.add_image :graphic1, "/path/to/the/image.jpg"
94
+ end
95
+ ```
96
+
97
+ ### Sections
98
+
99
+ Sometimes, you have to repeat a whole chunk of a document, in a structure a lot more complex than a table. You can make a Section in your template and use it in this situations. Creating a Section in OpenOffice is as easy as select menu *Insert* and then *Section...*, and then choose a name for it.
100
+
101
+ Sections are lot like Tables, in the sense that you can pass a collection and have that section repeated for each member of the collection. *But*, Sections can have anything inside it, even Tables *and nested Sections*, as long as you provide the appropriate data structure.
102
+
103
+ Let's see an example:
104
+
105
+ ```ruby
106
+ @invoices = Invoice.find(:all)
107
+
108
+ report = ODFReport::Report.new("reports/invoice.odt") do |r|
109
+
110
+ r.add_field(:title, "INVOICES REPORT")
111
+ r.add_field(:date, Date.today)
112
+
113
+ r.add_section("SC_INVOICE", @invoices) do |s|
114
+
115
+ s.add_field(:number) { |invoice| invoice.number.to_s.rjust(5, '0') }
116
+ s.add_field(:name, :customer_name)
117
+ s.add_field(:address, :customer_address)
118
+
119
+ s.add_table("TB_ITEMS", :items, header: true) do |t|
120
+ t.add_column(:id)
121
+ t.add_column(:product) {|item| item.product.name }
122
+ t.add_column(:value, :product_value)
123
+ end
124
+
125
+ s.add_field(:total) do |invoice|
126
+ if invoice.status == 'CLOSED'
127
+ invoice.total
128
+ else
129
+ invoice.items.sum('product_value')}
130
+ end
131
+ end
132
+
133
+ s.add_section("SUB_NOTES", :notes) do |s1|
134
+
135
+ s1.add_field(:note_title) { |n| n.title }
136
+
137
+ s1.add_table ...
138
+
139
+ end
140
+
141
+ end
142
+
143
+ end
144
+ ```
145
+
146
+ Note that when you add a Table to a Section, you don't pass the collection itself, but the attribute of the item of that section that will return the collection for that particular Table. Sounds complicated, huh? But once you get it, it's quite straightforward.
147
+
148
+ In the above example, `s.add_table("TB_ITEMS", :items, header: true) do |t|`, the `:items` thing refers to a `invoice.items`. Easy, right?
149
+
150
+ <hr/>
151
+
152
+ ### Step 2 -- generating the document
153
+
154
+ It's fairly simple to generate the document. You can use this inside a Rails application or in a standalone script.
155
+
156
+ #### Generating a document in a Rails application
157
+
158
+ In a controller, you can have a code like this:
159
+
160
+ ```ruby
161
+ def print
162
+
163
+ @ticket = Ticket.find(params[:id])
164
+
165
+ report = ODFReport::Report.new(Rails.root.join("/app/reports/ticket.odt") do |r|
166
+
167
+ r.add_field(:id, @ticket.id.to_s)
168
+ r.add_field(:created_by, @ticket.created_by)
169
+ r.add_field(:created_at, @ticket.created_at.strftime("%d/%m/%Y - %H:%M"))
170
+ r.add_field(:type, @ticket.type.name)
171
+ r.add_field(:status, @ticket.status_text)
172
+ r.add_field(:date, Time.now.strftime("%d/%m/%Y - %H:%M"))
173
+ r.add_field(:solution, (@ticket.solution || ''))
174
+
175
+ r.add_table("OPERATORS", @ticket.operators) do |t|
176
+ t.add_column(:name) { |op| "#{op.name} (#{op.department.short_name})" }
177
+ end
178
+
179
+ r.add_table("FIELDS", @ticket.fields) do |t|
180
+ t.add_column(:field_name, :name)
181
+ t.add_column(:field_value) { |field| field.text_value || "Empty" }
182
+ end
183
+
184
+ end
185
+
186
+ send_data report.generate,
187
+ type: 'application/vnd.oasis.opendocument.text',
188
+ disposition: 'attachment',
189
+ filename: 'report.odt'
190
+
191
+ end
192
+ ```
193
+
194
+ #### Generating a document in a standalone script
195
+
196
+ It's very similar to a Rails app, but you can inform the path where the file will be saved.
197
+
198
+ ```ruby
199
+ report = ODFReport::Report.new("ticket.odt") do |r|
200
+ ... populates the report ...
201
+ end
202
+
203
+ report.generate("./documents/new_ticket.odt")
204
+ ```
205
+
206
+ #### Using a template stored in the database (or anywhere besides the file system)
207
+ You can provide an `io:` param, containing the actual file read into a String.
208
+
209
+ ```ruby
210
+ report = ODFReport::Report.new(io: @template.attachment.read) do |r|
211
+ ```
212
+
213
+ <hr/>
214
+
215
+ #### REQUIREMENTS
216
+
217
+ **rubyzip**: for manipulating the contents of the odt file, since it's actually a zip file.
218
+ **nokogiri**: for parsing and manipulating the document xml files.
@@ -8,7 +8,7 @@ require File.expand_path('../odf-report/parser/default', __FILE__)
8
8
  require File.expand_path('../odf-report/images', __FILE__)
9
9
  require File.expand_path('../odf-report/field', __FILE__)
10
10
  require File.expand_path('../odf-report/text', __FILE__)
11
- require File.expand_path('../odf-report/file', __FILE__)
11
+ require File.expand_path('../odf-report/template', __FILE__)
12
12
  require File.expand_path('../odf-report/nested', __FILE__)
13
13
  require File.expand_path('../odf-report/section', __FILE__)
14
14
  require File.expand_path('../odf-report/table', __FILE__)
@@ -15,7 +15,7 @@ module ODFReport
15
15
 
16
16
  end
17
17
 
18
- def replace_images(file)
18
+ def include_image_files(file)
19
19
 
20
20
  return if @images.empty?
21
21
 
@@ -3,9 +3,9 @@ module ODFReport
3
3
  class Report
4
4
  include Images
5
5
 
6
- def initialize(template_name, &block)
6
+ def initialize(template_name = nil, io: nil)
7
7
 
8
- @file = ODFReport::File.new(template_name)
8
+ @template = ODFReport::Template.new(template_name, io: io)
9
9
 
10
10
  @texts = []
11
11
  @fields = []
@@ -14,7 +14,7 @@ class Report
14
14
  @image_names_replacements = {}
15
15
  @sections = []
16
16
 
17
- yield(self)
17
+ yield(self) if block_given?
18
18
 
19
19
  end
20
20
 
@@ -52,45 +52,33 @@ class Report
52
52
 
53
53
  def generate(dest = nil)
54
54
 
55
- @file.update_content do |file|
55
+ @template.update_content do |file|
56
56
 
57
- file.update_files('content.xml', 'styles.xml') do |txt|
57
+ file.update_files do |doc|
58
58
 
59
- parse_document(txt) do |doc|
59
+ @sections.each { |s| s.replace!(doc) }
60
+ @tables.each { |t| t.replace!(doc) }
60
61
 
61
- @sections.each { |s| s.replace!(doc) }
62
- @tables.each { |t| t.replace!(doc) }
62
+ @texts.each { |t| t.replace!(doc) }
63
+ @fields.each { |f| f.replace!(doc) }
63
64
 
64
- @texts.each { |t| t.replace!(doc) }
65
- @fields.each { |f| f.replace!(doc) }
66
-
67
- find_image_name_matches(doc)
68
- avoid_duplicate_image_names(doc)
69
-
70
- end
65
+ find_image_name_matches(doc)
66
+ avoid_duplicate_image_names(doc)
71
67
 
72
68
  end
73
69
 
74
- replace_images(file)
70
+ include_image_files(file)
75
71
 
76
72
  end
77
73
 
78
74
  if dest
79
- ::File.open(dest, "wb") {|f| f.write(@file.data) }
75
+ ::File.open(dest, "wb") {|f| f.write(@template.data) }
80
76
  else
81
- @file.data
77
+ @template.data
82
78
  end
83
79
 
84
80
  end
85
81
 
86
- private
87
-
88
- def parse_document(txt)
89
- doc = Nokogiri::XML(txt)
90
- yield doc
91
- txt.replace(doc.to_xml(:save_with => Nokogiri::XML::Node::SaveOptions::AS_XML))
92
- end
93
-
94
82
  end
95
83
 
96
84
  end
@@ -0,0 +1,68 @@
1
+ module ODFReport
2
+ class Template
3
+
4
+ CONTENT_FILES = ['content.xml', 'styles.xml']
5
+
6
+ attr_accessor :output_stream
7
+
8
+ def initialize(template = nil, io: nil)
9
+ raise "You must provide either a filename or an io: string" unless template || io
10
+ raise "Template [#{template}] not found." unless template.nil? || ::File.exist?(template)
11
+
12
+ @template = template
13
+ @io = io
14
+ end
15
+
16
+ def update_content
17
+ @buffer = Zip::OutputStream.write_buffer do |out|
18
+ @output_stream = out
19
+ yield self
20
+ end
21
+ end
22
+
23
+ def update_files(&block)
24
+
25
+ get_template_entries.each do |entry|
26
+
27
+ next if entry.directory?
28
+
29
+ entry.get_input_stream do |is|
30
+
31
+ data = is.sysread
32
+
33
+ if CONTENT_FILES.include?(entry.name)
34
+ process_entry(data, &block)
35
+ end
36
+
37
+ @output_stream.put_next_entry(entry.name)
38
+ @output_stream.write data
39
+
40
+ end
41
+ end
42
+
43
+ end
44
+
45
+ def data
46
+ @buffer.string
47
+ end
48
+
49
+ private
50
+
51
+ def get_template_entries
52
+
53
+ if @template
54
+ Zip::File.open(@template)
55
+ else
56
+ Zip::File.open_buffer(@io.force_encoding("ASCII-8BIT"))
57
+ end
58
+
59
+ end
60
+
61
+ def process_entry(entry)
62
+ doc = Nokogiri::XML(entry)
63
+ yield doc
64
+ entry.replace(doc.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::AS_XML))
65
+ end
66
+
67
+ end
68
+ end
@@ -1,3 +1,3 @@
1
1
  module ODFReport
2
- VERSION = "0.5.2"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -9,7 +9,6 @@ Gem::Specification.new do |s|
9
9
  s.authors = ["Sandro Duarte"]
10
10
  s.description = %q{Generates ODF files, given a template (.odt) and data, replacing tags}
11
11
  s.email = %q{sandrods@gmail.com}
12
- s.has_rdoc = false
13
12
  s.homepage = %q{http://sandrods.github.com/odf-report/}
14
13
  s.rubygems_version = %q{1.3.7}
15
14
  s.summary = %q{Generates ODF files, given a template (.odt) and data, replacing tags}
@@ -19,7 +18,7 @@ Gem::Specification.new do |s|
19
18
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
20
19
  s.require_paths = ["lib"]
21
20
 
22
- s.add_development_dependency "bundler", "~> 1.6"
21
+ s.add_development_dependency "bundler"
23
22
  s.add_development_dependency "rake"
24
23
  s.add_development_dependency "rspec", "~> 3.0.0"
25
24
  s.add_development_dependency "faker"
@@ -0,0 +1,45 @@
1
+ RSpec.describe "Templates Types" do
2
+
3
+ before(:each) do
4
+
5
+ @field_01 = Faker::Company.name
6
+ @field_02 = Faker::Name.name
7
+
8
+ report.add_field(:field_01, @field_01)
9
+ report.add_field(:field_02, @field_02)
10
+
11
+ report.generate("spec/result/specs.odt")
12
+
13
+ @data = Inspector.new("spec/result/specs.odt")
14
+
15
+ end
16
+
17
+ context "template from file" do
18
+ let(:report) { ODFReport::Report.new("spec/specs.odt") }
19
+
20
+ it "works" do
21
+
22
+ expect(@data.text).not_to match(/\[FIELD_01\]/)
23
+ expect(@data.text).not_to match(/\[FIELD_02\]/)
24
+
25
+ expect(@data.text).to match @field_01
26
+ expect(@data.text).to match @field_02
27
+
28
+ end
29
+ end
30
+
31
+ context "template from a String" do
32
+ let(:report) { ODFReport::Report.new(io: ::File.open("spec/specs.odt").read) }
33
+
34
+ it "works" do
35
+
36
+ expect(@data.text).not_to match(/\[FIELD_01\]/)
37
+ expect(@data.text).not_to match(/\[FIELD_02\]/)
38
+
39
+ expect(@data.text).to match @field_01
40
+ expect(@data.text).to match @field_02
41
+
42
+ end
43
+ end
44
+
45
+ end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: odf-report
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sandro Duarte
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-07 00:00:00.000000000 Z
11
+ date: 2019-01-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.6'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.6'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -119,17 +119,17 @@ files:
119
119
  - Gemfile
120
120
  - MIT-LICENSE
121
121
  - Manifest
122
- - README.textile
122
+ - README.md
123
123
  - Rakefile
124
124
  - lib/odf-report.rb
125
125
  - lib/odf-report/field.rb
126
- - lib/odf-report/file.rb
127
126
  - lib/odf-report/images.rb
128
127
  - lib/odf-report/nested.rb
129
128
  - lib/odf-report/parser/default.rb
130
129
  - lib/odf-report/report.rb
131
130
  - lib/odf-report/section.rb
132
131
  - lib/odf-report/table.rb
132
+ - lib/odf-report/template.rb
133
133
  - lib/odf-report/text.rb
134
134
  - lib/odf-report/version.rb
135
135
  - odf-report.gemspec
@@ -138,6 +138,7 @@ files:
138
138
  - spec/spec_helper.rb
139
139
  - spec/specs.odt
140
140
  - spec/tables_spec.rb
141
+ - spec/template_spec.rb
141
142
  - test/fields_inside_text_test.rb
142
143
  - test/nested_tables_test.rb
143
144
  - test/sections_test.rb
@@ -173,7 +174,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
173
174
  version: '0'
174
175
  requirements: []
175
176
  rubyforge_project:
176
- rubygems_version: 2.6.12
177
+ rubygems_version: 2.7.6
177
178
  signing_key:
178
179
  specification_version: 4
179
180
  summary: Generates ODF files, given a template (.odt) and data, replacing tags
@@ -183,6 +184,7 @@ test_files:
183
184
  - spec/spec_helper.rb
184
185
  - spec/specs.odt
185
186
  - spec/tables_spec.rb
187
+ - spec/template_spec.rb
186
188
  - test/fields_inside_text_test.rb
187
189
  - test/nested_tables_test.rb
188
190
  - test/sections_test.rb
@@ -1,223 +0,0 @@
1
- h1. ODF-REPORT
2
-
3
- Gem for generating .odt files by making strings, images, tables and sections replacements in a previously created .odt file.
4
-
5
- h3. NEW
6
-
7
- * as per popular request, now uses rubyzip ~> 1.1.0
8
- * uses @Zip::OutputStream.write_buffer@ to generate the file. This should avoid corruption issues.
9
- * the @.generate@ method now returns the actual report binary, so you can send_data it directly
10
- * you can also use @.generate('file.odt')@, which saves the report to the specified file
11
-
12
- h2. INSTALL
13
-
14
- In your Gemfile
15
- <pre>gem 'odf-report' </pre>
16
-
17
- h2. USAGE
18
-
19
- h3. Step 1 -- the template
20
-
21
- First of all, you need to create a .odt file to serve as a template.
22
- Templates are normal .odt files with placeholders for *substitutions*.
23
- There are now *four* kinds of substitutions available: *fields*, *tables*, *images* and *sections*.
24
-
25
- h3. Fields placeholders
26
-
27
- It's just an upcase sentence, surrounded by brackets. It will be replaced for wathever value you supply.
28
-
29
- In the folowing example:
30
-
31
- <pre>
32
- report = ODFReport::Report.new("Users/john/my_template.odt") do |r|
33
-
34
- r.add_field :user_name, @user.name
35
- r.add_field :address, "My new address"
36
-
37
- end
38
- </pre>
39
-
40
- 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@
41
-
42
- It's as simple as that.
43
-
44
-
45
- h3. Table placeholders
46
-
47
- 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.
48
-
49
- If you inform @:header=>true@, the first row will be treated as a *header* and left untouched. The remaining rows will be used as the template for the table. If you have more than one template row, they will be cycled. This is usefull for making zebra tables.
50
-
51
- As with Field placeholders, just insert a @[FIELD_NAME]@ in each cell and let the magic takes place.
52
-
53
- Taking the folowing example:
54
-
55
- <pre>
56
- report = ODFReport::Report.new("Users/john/my_template.odt") do |r|
57
-
58
- r.add_field "USER_NAME", @user.nome
59
- r.add_field "ADDRESS", @user.address
60
-
61
- r.add_table("TABLE_1", @list_of_itens, :header=>true) do |t|
62
- t.add_column(:item_id, :id)
63
- t.add_column(:description) { |item| "==> #{item.description}" }
64
- end
65
-
66
- end
67
- </pre>
68
-
69
- and considering you have a table like this in your template
70
-
71
- <pre>
72
- ---------------------------------
73
- | [ITEM_ID] | [DESCRIPTION] |
74
- ---------------------------------
75
-
76
- * this is my lame attempt to draw a table.
77
- you are not supposed to type this.
78
- you have to use an actual table.
79
- i don't know... just thought I should mention it ;-)
80
- </pre>
81
-
82
- and a collection @list_of_itens, it will create one row for each item in the collection, and the replacement will take place accordingly.
83
-
84
- Any format applied to the fields in the template will be preserved.
85
-
86
-
87
- h3. Images
88
-
89
- You must put a mock image in your odt template and give it a name. That name will be used to replace the mock image for the actual image.
90
- You can also assign any properties you want to the mock image and they will be kept once the image is replaced.
91
-
92
- An image replace would look like this:
93
-
94
- <pre>
95
- report = ODFReport::Report.new("Users/john/my_template.odt") do |r|
96
-
97
- r.add_image :graphics1, "/path/to/the/image.jpg"
98
-
99
- end
100
- </pre>
101
-
102
-
103
- h3. Sections
104
-
105
- Sometimes, you have to repeat a whole chunk of a document, in a structure a lot more complex than a table. Now you can make a Section in your template and use it in this situations. Creating a Section in OpenOffice is as easy as select menu *Insert* and then *Section...*, and then choose a name for it.
106
-
107
- *Section* 's are lot like Tables, in the sense that you can pass a collection and have that section repeated for each member of the collection. *But*, Sections can have anything inside it, even Tables *and nested Sections*, as long as you pass the appropriate data structure.
108
-
109
- Let's see an example:
110
-
111
- <pre>
112
-
113
- @invoices = Invoice.find(:all)
114
-
115
- report = ODFReport::Report.new("reports/invoice.odt") do |r|
116
-
117
- r.add_field(:title, "INVOICES REPORT")
118
- r.add_field(:date, Date.today)
119
-
120
- r.add_section("SC_INVOICE", @invoices) do |s|
121
-
122
- s.add_field(:number) { |invoice| invoice.number.to_s.rjust(5, '0') }
123
- s.add_field(:name, :customer_name)
124
- s.add_field(:address, :customer_address)
125
-
126
- s.add_table("TB_ITEMS", :items, :header => true) do |t|
127
- t.add_column(:id)
128
- t.add_column(:product) {|item| item.product.name }
129
- t.add_column(:value, :product_value)
130
- end
131
-
132
- s.add_field(:total) do |invoice|
133
- if invoice.status == 'CLOSED'
134
- invoice.total
135
- else
136
- invoice.items.sum('product_value')}
137
- end
138
- end
139
-
140
- s.add_section("SUB_NOTES", :notes) do |s1|
141
-
142
- s1.add_field(:note_title) { |n| n.title }
143
-
144
- s1.add_table ...
145
-
146
- end
147
-
148
- end
149
-
150
- end
151
- </pre>
152
-
153
- Note that when you add a Table to a Section, you don't pass the collection itself, but the attribute of the item of that section that's gonna return the collection for that particular Table. Sounds complicated, huh? But once you get it, it's quite straightforward.
154
-
155
- In the above example, @s.add_table("TB_ITEMS", :items, :header => true) do |t|@, the @:items@ thing refers to a @invoice.items@. Easy, right?
156
-
157
- <hr/><br/>
158
-
159
- h3. Step 2 -- generating the document
160
-
161
- It's fairly simple to generate the document. You can use this inside a Rails application or in a standalone script.
162
-
163
- h4. Generating a document in a Rails application
164
-
165
- In a controller, you can have a code like this:
166
-
167
- <pre>
168
- def print
169
-
170
- @ticket = Ticket.find(params[:id])
171
-
172
- # For Rails 3 or latest replace #{RAILS_ROOT} to #{Rails.root}
173
- report = ODFReport::Report.new("#{RAILS_ROOT}/app/reports/ticket.odt") do |r|
174
-
175
- r.add_field(:id, @ticket.id.to_s)
176
- r.add_field(:created_by, @ticket.created_by)
177
- r.add_field(:created_at, @ticket.created_at.strftime("%d/%m/%Y - %H:%M"))
178
- r.add_field(:type, @ticket.type.name)
179
- r.add_field(:status, @ticket.status_text)
180
- r.add_field(:date, Time.now.strftime("%d/%m/%Y - %H:%M"))
181
- r.add_field(:solution, (@ticket.solution || ''))
182
-
183
- r.add_table("OPERATORS", @ticket.operators) do |t|
184
- t.add_column(:operator_name) { |op| "#{op.name} (#{op.department.short_name})" }
185
- end
186
-
187
- r.add_table("FIELDS", @ticket.fields) do |t|
188
- t.add_column(:field_name, :name)
189
- t.add_column(:field_value) { |field| field.text_value || "Empty" }
190
- end
191
-
192
- end
193
-
194
- send_data report.generate, type: 'application/vnd.oasis.opendocument.text',
195
- disposition: 'attachment',
196
- filename: 'report.odt'
197
-
198
- end
199
- </pre>
200
-
201
-
202
- _That's all I have to say about that._
203
-
204
- h4. Generating a document in a standalone script
205
-
206
- It's just the same as in a Rails app, but you can inform the path where the file will be saved.
207
-
208
- <pre>
209
- report = ODFReport::Report.new("ticket.odt") do |r|
210
-
211
- ... populates the report ...
212
-
213
- end
214
-
215
- report.generate("./documents/new_ticket.odt")
216
- </pre>
217
-
218
- <hr/>
219
-
220
- h3. REQUIREMENTS
221
-
222
- * rubyzip*: for manipulating the contents of the odt file, since it's actually a zip file.
223
- * nokogiri*: for parsing and manipulating the document xml files.
@@ -1,50 +0,0 @@
1
- module ODFReport
2
- class File
3
-
4
- attr_accessor :output_stream
5
-
6
- def initialize(template)
7
- raise "Template [#{template}] not found." unless ::File.exist? template
8
- @template = template
9
- end
10
-
11
- def update_content
12
- @buffer = Zip::OutputStream.write_buffer do |out|
13
- @output_stream = out
14
- yield self
15
- end
16
- end
17
-
18
- def update_files(*content_files, &block)
19
-
20
- Zip::File.open(@template) do |file|
21
-
22
- file.each do |entry|
23
-
24
- next if entry.directory?
25
-
26
- entry.get_input_stream do |is|
27
-
28
- data = is.sysread
29
-
30
- if content_files.include?(entry.name)
31
- yield data
32
- end
33
-
34
- @output_stream.put_next_entry(entry.name)
35
- @output_stream.write data
36
-
37
- end
38
-
39
- end
40
-
41
- end
42
-
43
- end
44
-
45
- def data
46
- @buffer.string
47
- end
48
-
49
- end
50
- end